summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/adapter/containers.go36
-rw-r--r--pkg/adapter/containers_remote.go6
-rw-r--r--pkg/adapter/pods.go176
-rw-r--r--pkg/adapter/pods_remote.go2
-rw-r--r--pkg/adapter/runtime.go6
-rw-r--r--pkg/adapter/runtime_remote.go6
-rw-r--r--pkg/adapter/shortcuts/shortcuts.go32
-rw-r--r--pkg/hooks/docs/oci-hooks.5.md2
-rw-r--r--pkg/spec/config_linux_cgo.go2
-rw-r--r--pkg/spec/config_linux_nocgo.go2
-rw-r--r--pkg/spec/config_unsupported.go2
-rw-r--r--pkg/spec/createconfig.go420
-rw-r--r--pkg/spec/namespaces.go433
-rw-r--r--pkg/spec/security.go172
-rw-r--r--pkg/spec/spec.go315
-rw-r--r--pkg/spec/spec_test.go6
-rw-r--r--pkg/spec/storage.go14
-rw-r--r--pkg/timetype/timestamp.go131
-rw-r--r--pkg/timetype/timestamp_test.go95
-rw-r--r--pkg/trust/trust.go2
-rw-r--r--pkg/util/utils.go2
-rw-r--r--pkg/varlinkapi/images.go8
22 files changed, 1231 insertions, 639 deletions
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index 287bd8474..bc9554193 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -79,8 +79,18 @@ func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopVa
}
logrus.Debugf("Setting maximum stop workers to %d", maxWorkers)
- ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime)
- if err != nil {
+ names := cli.InputArgs
+ for _, cidFile := range cli.CIDFiles {
+ content, err := ioutil.ReadFile(cidFile)
+ if err != nil {
+ return nil, nil, errors.Wrap(err, "error reading CIDFile")
+ }
+ id := strings.Split(string(content), "\n")[0]
+ names = append(names, id)
+ }
+
+ ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, names, r.Runtime)
+ if err != nil && !(cli.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
return nil, nil, err
}
@@ -203,8 +213,18 @@ func (r *LocalRuntime) RemoveContainers(ctx context.Context, cli *cliconfig.RmVa
return ok, failures, nil
}
- ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime)
- if err != nil {
+ names := cli.InputArgs
+ for _, cidFile := range cli.CIDFiles {
+ content, err := ioutil.ReadFile(cidFile)
+ if err != nil {
+ return nil, nil, errors.Wrap(err, "error reading CIDFile")
+ }
+ id := strings.Split(string(content), "\n")[0]
+ names = append(names, id)
+ }
+
+ ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, names, r.Runtime)
+ if err != nil && !(cli.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
// Failed to get containers. If force is specified, get the containers ID
// and evict them
if !cli.Force {
@@ -215,6 +235,10 @@ func (r *LocalRuntime) RemoveContainers(ctx context.Context, cli *cliconfig.RmVa
logrus.Debugf("Evicting container %q", ctr)
id, err := r.EvictContainer(ctx, ctr, cli.Volumes)
if err != nil {
+ if cli.Ignore && errors.Cause(err) == define.ErrNoSuchCtr {
+ logrus.Debugf("Ignoring error (--allow-missing): %v", err)
+ continue
+ }
failures[ctr] = errors.Wrapf(err, "Failed to evict container: %q", id)
continue
}
@@ -232,6 +256,10 @@ func (r *LocalRuntime) RemoveContainers(ctx context.Context, cli *cliconfig.RmVa
Fn: func() error {
err := r.RemoveContainer(ctx, c, cli.Force, cli.Volumes)
if err != nil {
+ if cli.Ignore && errors.Cause(err) == define.ErrNoSuchCtr {
+ logrus.Debugf("Ignoring error (--allow-missing): %v", err)
+ return nil
+ }
logrus.Debugf("Failed to remove container %s: %s", c.ID(), err.Error())
}
return err
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index 20471d895..e34b8ffd9 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -178,7 +178,7 @@ func (r *LocalRuntime) LookupContainersWithStatus(filters []string) ([]*Containe
if err != nil {
return nil, err
}
- // This is not performance savy; if this turns out to be a problematic series of lookups, we need to
+ // This is not performance savvy; if this turns out to be a problematic series of lookups, we need to
// create a new endpoint to speed things up
for _, ctr := range ctrs {
container, err := r.LookupContainer(ctr.Id)
@@ -617,7 +617,7 @@ func (r *LocalRuntime) Checkpoint(c *cliconfig.CheckpointValues) error {
return err
}
if c.All {
- // We dont have a great way to get all the running containers, so need to get all and then
+ // We don't have a great way to get all the running containers, so need to get all and then
// check status on them bc checkpoint considers checkpointing a stopped container an error
var runningIds []string
for _, id := range ids {
@@ -660,7 +660,7 @@ func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues)
return err
}
if c.All {
- // We dont have a great way to get all the exited containers, so need to get all and then
+ // We don't have a great way to get all the exited containers, so need to get all and then
// check status on them bc checkpoint considers restoring a running container an error
var exitedIDs []string
for _, id := range ids {
diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go
index 6648edc82..e9f3d41a9 100644
--- a/pkg/adapter/pods.go
+++ b/pkg/adapter/pods.go
@@ -15,8 +15,10 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/adapter/shortcuts"
+ ann "github.com/containers/libpod/pkg/annotations"
ns "github.com/containers/libpod/pkg/namespaces"
createconfig "github.com/containers/libpod/pkg/spec"
"github.com/containers/libpod/pkg/util"
@@ -36,7 +38,7 @@ const (
)
// PodContainerStats is struct containing an adapter Pod and a libpod
-// ContainerStats and is used primarily for outputing pod stats.
+// ContainerStats and is used primarily for outputting pod stats.
type PodContainerStats struct {
Pod *Pod
ContainerStats map[string]*libpod.ContainerStats
@@ -93,7 +95,7 @@ func (r *LocalRuntime) RemovePods(ctx context.Context, cli *cliconfig.PodRmValue
podids []string
)
pods, err := shortcuts.GetPodsByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime)
- if err != nil {
+ if err != nil && !(cli.Ignore && errors.Cause(err) == define.ErrNoSuchPod) {
errs = append(errs, err)
return nil, errs
}
@@ -150,7 +152,7 @@ func (r *LocalRuntime) StopPods(ctx context.Context, cli *cliconfig.PodStopValue
podids []string
)
pods, err := shortcuts.GetPodsByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime)
- if err != nil {
+ if err != nil && !(cli.Ignore && errors.Cause(err) == define.ErrNoSuchPod) {
errs = append(errs, err)
return nil, errs
}
@@ -595,12 +597,17 @@ func (r *LocalRuntime) PlayKubeYAML(ctx context.Context, c *cliconfig.KubePlayVa
volumes[volume.Name] = hostPath.Path
}
+ seccompPaths, err := initializeSeccompPaths(podYAML.ObjectMeta.Annotations)
+ if err != nil {
+ return nil, err
+ }
+
for _, container := range podYAML.Spec.Containers {
newImage, err := r.ImageRuntime().New(ctx, container.Image, c.SignaturePolicy, c.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageMissing)
if err != nil {
return nil, err
}
- createConfig, err := kubeContainerToCreateConfig(ctx, container, r.Runtime, newImage, namespaces, volumes, pod.ID())
+ createConfig, err := kubeContainerToCreateConfig(ctx, container, r.Runtime, newImage, namespaces, volumes, pod.ID(), podInfraID, seccompPaths)
if err != nil {
return nil, err
}
@@ -666,62 +673,69 @@ func getPodPorts(containers []v1.Container) []ocicni.PortMapping {
return infraPorts
}
-func setupSecurityContext(containerConfig *createconfig.CreateConfig, containerYAML v1.Container) {
+func setupSecurityContext(securityConfig *createconfig.SecurityConfig, userConfig *createconfig.UserConfig, containerYAML v1.Container) {
if containerYAML.SecurityContext == nil {
return
}
if containerYAML.SecurityContext.ReadOnlyRootFilesystem != nil {
- containerConfig.ReadOnlyRootfs = *containerYAML.SecurityContext.ReadOnlyRootFilesystem
+ securityConfig.ReadOnlyRootfs = *containerYAML.SecurityContext.ReadOnlyRootFilesystem
}
if containerYAML.SecurityContext.Privileged != nil {
- containerConfig.Privileged = *containerYAML.SecurityContext.Privileged
+ securityConfig.Privileged = *containerYAML.SecurityContext.Privileged
}
if containerYAML.SecurityContext.AllowPrivilegeEscalation != nil {
- containerConfig.NoNewPrivs = !*containerYAML.SecurityContext.AllowPrivilegeEscalation
+ securityConfig.NoNewPrivs = !*containerYAML.SecurityContext.AllowPrivilegeEscalation
}
if seopt := containerYAML.SecurityContext.SELinuxOptions; seopt != nil {
if seopt.User != "" {
- containerConfig.SecurityOpts = append(containerConfig.SecurityOpts, fmt.Sprintf("label=user:%s", seopt.User))
- containerConfig.LabelOpts = append(containerConfig.LabelOpts, fmt.Sprintf("user:%s", seopt.User))
+ securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=user:%s", seopt.User))
+ securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("user:%s", seopt.User))
}
if seopt.Role != "" {
- containerConfig.SecurityOpts = append(containerConfig.SecurityOpts, fmt.Sprintf("label=role:%s", seopt.Role))
- containerConfig.LabelOpts = append(containerConfig.LabelOpts, fmt.Sprintf("role:%s", seopt.Role))
+ securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=role:%s", seopt.Role))
+ securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("role:%s", seopt.Role))
}
if seopt.Type != "" {
- containerConfig.SecurityOpts = append(containerConfig.SecurityOpts, fmt.Sprintf("label=type:%s", seopt.Type))
- containerConfig.LabelOpts = append(containerConfig.LabelOpts, fmt.Sprintf("type:%s", seopt.Type))
+ securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=type:%s", seopt.Type))
+ securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("type:%s", seopt.Type))
}
if seopt.Level != "" {
- containerConfig.SecurityOpts = append(containerConfig.SecurityOpts, fmt.Sprintf("label=level:%s", seopt.Level))
- containerConfig.LabelOpts = append(containerConfig.LabelOpts, fmt.Sprintf("level:%s", seopt.Level))
+ securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=level:%s", seopt.Level))
+ securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("level:%s", seopt.Level))
}
}
if caps := containerYAML.SecurityContext.Capabilities; caps != nil {
for _, capability := range caps.Add {
- containerConfig.CapAdd = append(containerConfig.CapAdd, string(capability))
+ securityConfig.CapAdd = append(securityConfig.CapAdd, string(capability))
}
for _, capability := range caps.Drop {
- containerConfig.CapDrop = append(containerConfig.CapDrop, string(capability))
+ securityConfig.CapDrop = append(securityConfig.CapDrop, string(capability))
}
}
if containerYAML.SecurityContext.RunAsUser != nil {
- containerConfig.User = fmt.Sprintf("%d", *containerYAML.SecurityContext.RunAsUser)
+ userConfig.User = fmt.Sprintf("%d", *containerYAML.SecurityContext.RunAsUser)
}
if containerYAML.SecurityContext.RunAsGroup != nil {
- if containerConfig.User == "" {
- containerConfig.User = "0"
+ if userConfig.User == "" {
+ userConfig.User = "0"
}
- containerConfig.User = fmt.Sprintf("%s:%d", containerConfig.User, *containerYAML.SecurityContext.RunAsGroup)
+ userConfig.User = fmt.Sprintf("%s:%d", userConfig.User, *containerYAML.SecurityContext.RunAsGroup)
}
}
// kubeContainerToCreateConfig takes a v1.Container and returns a createconfig describing a container
-func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container, runtime *libpod.Runtime, newImage *image.Image, namespaces map[string]string, volumes map[string]string, podID string) (*createconfig.CreateConfig, error) {
+func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container, runtime *libpod.Runtime, newImage *image.Image, namespaces map[string]string, volumes map[string]string, podID, infraID string, seccompPaths *kubeSeccompPaths) (*createconfig.CreateConfig, error) {
var (
containerConfig createconfig.CreateConfig
+ pidConfig createconfig.PidConfig
+ networkConfig createconfig.NetworkConfig
+ cgroupConfig createconfig.CgroupConfig
+ utsConfig createconfig.UtsConfig
+ ipcConfig createconfig.IpcConfig
+ userConfig createconfig.UserConfig
+ securityConfig createconfig.SecurityConfig
)
// The default for MemorySwappiness is -1, not 0
@@ -737,18 +751,14 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container
imageData, _ := newImage.Inspect(ctx)
- containerConfig.User = "0"
+ userConfig.User = "0"
if imageData != nil {
- containerConfig.User = imageData.Config.User
+ userConfig.User = imageData.Config.User
}
- setupSecurityContext(&containerConfig, containerYAML)
+ setupSecurityContext(&securityConfig, &userConfig, containerYAML)
- var err error
- containerConfig.SeccompProfilePath, err = libpod.DefaultSeccompPath()
- if err != nil {
- return nil, err
- }
+ securityConfig.SeccompProfilePath = seccompPaths.findForContainer(containerConfig.Name)
containerConfig.Command = []string{}
if imageData != nil && imageData.Config != nil {
@@ -768,23 +778,38 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container
containerConfig.StopSignal = 15
// If the user does not pass in ID mappings, just set to basics
- if containerConfig.IDMappings == nil {
- containerConfig.IDMappings = &storage.IDMappingOptions{}
+ if userConfig.IDMappings == nil {
+ userConfig.IDMappings = &storage.IDMappingOptions{}
}
- containerConfig.NetMode = ns.NetworkMode(namespaces["net"])
- containerConfig.IpcMode = ns.IpcMode(namespaces["ipc"])
- containerConfig.UtsMode = ns.UTSMode(namespaces["uts"])
+ networkConfig.NetMode = ns.NetworkMode(namespaces["net"])
+ ipcConfig.IpcMode = ns.IpcMode(namespaces["ipc"])
+ utsConfig.UtsMode = ns.UTSMode(namespaces["uts"])
// disabled in code review per mheon
//containerConfig.PidMode = ns.PidMode(namespaces["pid"])
- containerConfig.UsernsMode = ns.UsernsMode(namespaces["user"])
+ userConfig.UsernsMode = ns.UsernsMode(namespaces["user"])
if len(containerConfig.WorkDir) == 0 {
containerConfig.WorkDir = "/"
}
+ containerConfig.Pid = pidConfig
+ containerConfig.Network = networkConfig
+ containerConfig.Uts = utsConfig
+ containerConfig.Ipc = ipcConfig
+ containerConfig.Cgroup = cgroupConfig
+ containerConfig.User = userConfig
+ containerConfig.Security = securityConfig
+
// Set default environment variables and incorporate data from image, if necessary
envs := shared.EnvVariablesFromData(imageData)
+ annotations := make(map[string]string)
+ if infraID != "" {
+ annotations[ann.SandboxID] = infraID
+ annotations[ann.ContainerType] = ann.ContainerTypeContainer
+ }
+ containerConfig.Annotations = annotations
+
// Environment Variables
for _, e := range containerYAML.Env {
envs[e.Name] = e.Value
@@ -803,3 +828,80 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container
}
return &containerConfig, nil
}
+
+// kubeSeccompPaths holds information about a pod YAML's seccomp configuration
+// it holds both container and pod seccomp paths
+type kubeSeccompPaths struct {
+ containerPaths map[string]string
+ podPath string
+}
+
+// findForContainer checks whether a container has a seccomp path configured for it
+// if not, it returns the podPath, which should always have a value
+func (k *kubeSeccompPaths) findForContainer(ctrName string) string {
+ if path, ok := k.containerPaths[ctrName]; ok {
+ return path
+ }
+ return k.podPath
+}
+
+// initializeSeccompPaths takes annotations from the pod object metadata and finds annotations pertaining to seccomp
+// it parses both pod and container level
+func initializeSeccompPaths(annotations map[string]string) (*kubeSeccompPaths, error) {
+ seccompPaths := &kubeSeccompPaths{containerPaths: make(map[string]string)}
+ var err error
+ if annotations != nil {
+ for annKeyValue, seccomp := range annotations {
+ // check if it is prefaced with container.seccomp.security.alpha.kubernetes.io/
+ prefixAndCtr := strings.Split(annKeyValue, "/")
+ if prefixAndCtr[0]+"/" != v1.SeccompContainerAnnotationKeyPrefix {
+ continue
+ } else if len(prefixAndCtr) != 2 {
+ // this could be caused by a user inputting either of
+ // container.seccomp.security.alpha.kubernetes.io{,/}
+ // both of which are invalid
+ return nil, errors.Errorf("Invalid seccomp path: %s", prefixAndCtr[0])
+ }
+
+ path, err := verifySeccompPath(seccomp)
+ if err != nil {
+ return nil, err
+ }
+ seccompPaths.containerPaths[prefixAndCtr[1]] = path
+ }
+
+ podSeccomp, ok := annotations[v1.SeccompPodAnnotationKey]
+ if ok {
+ seccompPaths.podPath, err = verifySeccompPath(podSeccomp)
+ } else {
+ seccompPaths.podPath, err = libpod.DefaultSeccompPath()
+ }
+ if err != nil {
+ return nil, err
+ }
+ }
+ return seccompPaths, nil
+}
+
+// verifySeccompPath takes a path and checks whether it is a default, unconfined, or a path
+// the available options are parsed as defined in https://kubernetes.io/docs/concepts/policy/pod-security-policy/#seccomp
+func verifySeccompPath(path string) (string, error) {
+ switch path {
+ case v1.DeprecatedSeccompProfileDockerDefault:
+ fallthrough
+ case v1.SeccompProfileRuntimeDefault:
+ return libpod.DefaultSeccompPath()
+ case "unconfined":
+ return path, nil
+ default:
+ // TODO we have an inconsistency here
+ // k8s parses `localhost/<path>` which is found at `<seccomp_root>`
+ // we currently parse `localhost:<seccomp_root>/<path>
+ // to fully conform, we need to find a good location for the seccomp root
+ parts := strings.Split(path, ":")
+ if parts[0] == "localhost" {
+ return parts[1], nil
+ }
+ return "", errors.Errorf("invalid seccomp path: %s", path)
+ }
+}
diff --git a/pkg/adapter/pods_remote.go b/pkg/adapter/pods_remote.go
index 0c62ac923..16d34769e 100644
--- a/pkg/adapter/pods_remote.go
+++ b/pkg/adapter/pods_remote.go
@@ -19,7 +19,7 @@ import (
)
// PodContainerStats is struct containing an adapter Pod and a libpod
-// ContainerStats and is used primarily for outputing pod stats.
+// ContainerStats and is used primarily for outputting pod stats.
type PodContainerStats struct {
Pod *Pod
ContainerStats map[string]*libpod.ContainerStats
diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go
index 81a43853c..069283bde 100644
--- a/pkg/adapter/runtime.go
+++ b/pkg/adapter/runtime.go
@@ -27,7 +27,7 @@ import (
"github.com/containers/libpod/pkg/util"
"github.com/containers/storage/pkg/archive"
"github.com/pkg/errors"
- "k8s.io/api/core/v1"
+ v1 "k8s.io/api/core/v1"
)
// LocalRuntime describes a typical libpod runtime
@@ -147,8 +147,8 @@ func (r *LocalRuntime) RemoveImage(ctx context.Context, img *ContainerImage, for
}
// PruneImages is wrapper into PruneImages within the image pkg
-func (r *LocalRuntime) PruneImages(ctx context.Context, all bool) ([]string, error) {
- return r.ImageRuntime().PruneImages(ctx, all)
+func (r *LocalRuntime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) {
+ return r.ImageRuntime().PruneImages(ctx, all, filter)
}
// Export is a wrapper to container export to a tarfile
diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go
index 12bf550f2..ddd4b5271 100644
--- a/pkg/adapter/runtime_remote.go
+++ b/pkg/adapter/runtime_remote.go
@@ -415,8 +415,8 @@ func (ci *ContainerImage) History(ctx context.Context) ([]*image.History, error)
}
// PruneImages is the wrapper call for a remote-client to prune images
-func (r *LocalRuntime) PruneImages(ctx context.Context, all bool) ([]string, error) {
- return iopodman.ImagesPrune().Call(r.Conn, all)
+func (r *LocalRuntime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) {
+ return iopodman.ImagesPrune().Call(r.Conn, all, filter)
}
// Export is a wrapper to container export to a tarfile
@@ -450,7 +450,7 @@ func (r *LocalRuntime) GetFileFromRemoteHost(remoteFilePath, outputPath string,
reader := r.Conn.Reader
if _, err := io.CopyN(writer, reader, length); err != nil {
- return errors.Wrap(err, "file transer failed")
+ return errors.Wrap(err, "file transfer failed")
}
return nil
}
diff --git a/pkg/adapter/shortcuts/shortcuts.go b/pkg/adapter/shortcuts/shortcuts.go
index 3e4eff555..4f6cfd6a3 100644
--- a/pkg/adapter/shortcuts/shortcuts.go
+++ b/pkg/adapter/shortcuts/shortcuts.go
@@ -2,9 +2,11 @@ package shortcuts
import (
"github.com/containers/libpod/libpod"
+ "github.com/sirupsen/logrus"
)
-// GetPodsByContext gets pods whether all, latest, or a slice of names/ids
+// 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 {
@@ -18,17 +20,24 @@ func GetPodsByContext(all, latest bool, pods []string, runtime *libpod.Runtime)
outpods = append(outpods, p)
return outpods, nil
}
+ var err error
for _, p := range pods {
- pod, err := runtime.LookupPod(p)
- if err != nil {
- return nil, err
+ 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)
}
- outpods = append(outpods, pod)
}
- return outpods, nil
+ return outpods, err
}
// 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{}
@@ -41,10 +50,15 @@ func GetContainersByContext(all, latest bool, names []string, runtime *libpod.Ru
} else {
for _, n := range names {
ctr, e := runtime.LookupContainer(n)
- if e != nil && err == nil {
- err = e
+ 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)
}
- ctrs = append(ctrs, ctr)
}
}
return
diff --git a/pkg/hooks/docs/oci-hooks.5.md b/pkg/hooks/docs/oci-hooks.5.md
index 0a01e1bb8..b50a6bddc 100644
--- a/pkg/hooks/docs/oci-hooks.5.md
+++ b/pkg/hooks/docs/oci-hooks.5.md
@@ -21,7 +21,7 @@ The default directory is `/usr/share/containers/oci/hooks.d`, but tools consumin
If multiple directories are configured, a JSON filename in a preferred directory masks entries with the same filename in directories with lower precedence. For example, if a consuming tool watches for hooks in `/etc/containers/oci/hooks.d` and `/usr/share/containers/oci/hooks.d` (in order of decreasing precedence), then a hook definition in `/etc/containers/oci/hooks.d/01-my-hook.json` will mask any definition in `/usr/share/containers/oci/hooks.d/01-my-hook.json`.
-Tools consuming this format may also opt to monitor the hook directries for changes, in which case they will notice additions, changes, and removals to JSON files without needing to be restarted or otherwise signaled. When the tool monitors multiple hooks directories, the precedence discussed in the previous paragraph still applies. For example, if a consuming tool watches for hooks in `/etc/containers/oci/hooks.d` and `/usr/share/containers/oci/hooks.d` (in order of decreasing precedence), then writing a new hook definition to `/etc/containers/oci/hooks.d/01-my-hook.json` will mask the hook previously loaded from `/usr/share/containers/oci/hooks.d/01-my-hook.json`. Subsequent changes to `/usr/share/containers/oci/hooks.d/01-my-hook.json` will have no effect on the consuming tool as long as `/etc/containers/oci/hooks.d/01-my-hook.json` exists. Removing `/etc/containers/oci/hooks.d/01-my-hook.json` will reload the hook from `/usr/share/containers/oci/hooks.d/01-my-hook.json`.
+Tools consuming this format may also opt to monitor the hook directories for changes, in which case they will notice additions, changes, and removals to JSON files without needing to be restarted or otherwise signaled. When the tool monitors multiple hooks directories, the precedence discussed in the previous paragraph still applies. For example, if a consuming tool watches for hooks in `/etc/containers/oci/hooks.d` and `/usr/share/containers/oci/hooks.d` (in order of decreasing precedence), then writing a new hook definition to `/etc/containers/oci/hooks.d/01-my-hook.json` will mask the hook previously loaded from `/usr/share/containers/oci/hooks.d/01-my-hook.json`. Subsequent changes to `/usr/share/containers/oci/hooks.d/01-my-hook.json` will have no effect on the consuming tool as long as `/etc/containers/oci/hooks.d/01-my-hook.json` exists. Removing `/etc/containers/oci/hooks.d/01-my-hook.json` will reload the hook from `/usr/share/containers/oci/hooks.d/01-my-hook.json`.
Hooks are injected in the order obtained by sorting the JSON file names, after converting them to lower case, based on their Unicode code points.
For example, a matching hook defined in `01-my-hook.json` would be injected before matching hooks defined in `02-another-hook.json` and `01-UPPERCASE.json`.
diff --git a/pkg/spec/config_linux_cgo.go b/pkg/spec/config_linux_cgo.go
index a1527752a..c47156456 100644
--- a/pkg/spec/config_linux_cgo.go
+++ b/pkg/spec/config_linux_cgo.go
@@ -10,7 +10,7 @@ import (
seccomp "github.com/seccomp/containers-golang"
)
-func getSeccompConfig(config *CreateConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
+func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
var seccompConfig *spec.LinuxSeccomp
var err error
diff --git a/pkg/spec/config_linux_nocgo.go b/pkg/spec/config_linux_nocgo.go
index 10329ff3b..8d720b6d4 100644
--- a/pkg/spec/config_linux_nocgo.go
+++ b/pkg/spec/config_linux_nocgo.go
@@ -6,6 +6,6 @@ import (
spec "github.com/opencontainers/runtime-spec/specs-go"
)
-func getSeccompConfig(config *CreateConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
+func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
return nil, nil
}
diff --git a/pkg/spec/config_unsupported.go b/pkg/spec/config_unsupported.go
index 160414878..a2c7f4416 100644
--- a/pkg/spec/config_unsupported.go
+++ b/pkg/spec/config_unsupported.go
@@ -8,7 +8,7 @@ import (
"github.com/pkg/errors"
)
-func getSeccompConfig(config *CreateConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
+func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
return nil, errors.New("function not supported on non-linux OS's")
}
func addDevice(g *generate.Generator, device string) error {
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index e054b3b13..244a8d1cd 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -1,7 +1,6 @@
package createconfig
import (
- "net"
"os"
"strconv"
"strings"
@@ -12,7 +11,6 @@ import (
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/namespaces"
"github.com/containers/storage"
- "github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-connections/nat"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
@@ -55,89 +53,126 @@ type CreateResourceConfig struct {
Ulimit []string //ulimit
}
-// CreateConfig is a pre OCI spec structure. It represents user input from varlink or the CLI
-type CreateConfig struct {
- Annotations map[string]string
- Args []string
+// PidConfig configures the pid namespace for the container
+type PidConfig struct {
+ PidMode namespaces.PidMode //pid
+}
+
+// IpcConfig configures the ipc namespace for the container
+type IpcConfig struct {
+ IpcMode namespaces.IpcMode //ipc
+}
+
+// CgroupConfig configures the cgroup namespace for the container
+type CgroupConfig struct {
+ Cgroups string
+ Cgroupns string
+ CgroupParent string // cgroup-parent
+ CgroupMode namespaces.CgroupMode //cgroup
+}
+
+// UserConfig configures the user namespace for the container
+type UserConfig struct {
+ GroupAdd []string // group-add
+ IDMappings *storage.IDMappingOptions
+ UsernsMode namespaces.UsernsMode //userns
+ User string //user
+}
+
+// UtsConfig configures the uts namespace for the container
+type UtsConfig struct {
+ UtsMode namespaces.UTSMode //uts
+ NoHosts bool
+ HostAdd []string //add-host
+ Hostname string
+}
+
+// NetworkConfig configures the network namespace for the container
+type NetworkConfig struct {
+ DNSOpt []string //dns-opt
+ DNSSearch []string //dns-search
+ DNSServers []string //dns
+ ExposedPorts map[nat.Port]struct{}
+ HTTPProxy bool
+ IP6Address string //ipv6
+ IPAddress string //ip
+ LinkLocalIP []string // link-local-ip
+ MacAddress string //mac-address
+ NetMode namespaces.NetworkMode //net
+ Network string //network
+ NetworkAlias []string //network-alias
+ PortBindings nat.PortMap
+ Publish []string //publish
+ PublishAll bool //publish-all
+}
+
+// SecurityConfig configures the security features for the container
+type SecurityConfig struct {
CapAdd []string // cap-add
CapDrop []string // cap-drop
- CidFile string
- ConmonPidFile string
- Cgroupns string
- Cgroups string
- CgroupParent string // cgroup-parent
- Command []string // Full command that will be used
- UserCommand []string // User-entered command (or image CMD)
- Detach bool // detach
- Devices []string // device
- DNSOpt []string //dns-opt
- DNSSearch []string //dns-search
- DNSServers []string //dns
- Entrypoint []string //entrypoint
- Env map[string]string //env
- ExposedPorts map[nat.Port]struct{}
- GroupAdd []string // group-add
- HealthCheck *manifest.Schema2HealthConfig
- NoHosts bool
- HostAdd []string //add-host
- Hostname string //hostname
- HTTPProxy bool
- Init bool // init
- InitPath string //init-path
- Image string
- ImageID string
- BuiltinImgVolumes map[string]struct{} // volumes defined in the image config
- IDMappings *storage.IDMappingOptions
- ImageVolumeType string // how to handle the image volume, either bind, tmpfs, or ignore
- Interactive bool //interactive
- IpcMode namespaces.IpcMode //ipc
- IP6Address string //ipv6
- IPAddress string //ip
- Labels map[string]string //label
- LinkLocalIP []string // link-local-ip
- LogDriver string // log-driver
- LogDriverOpt []string // log-opt
- MacAddress string //mac-address
- Name string //name
- NetMode namespaces.NetworkMode //net
- Network string //network
- NetworkAlias []string //network-alias
- PidMode namespaces.PidMode //pid
- Pod string //pod
- PodmanPath string
- CgroupMode namespaces.CgroupMode //cgroup
- PortBindings nat.PortMap
- Privileged bool //privileged
- Publish []string //publish
- PublishAll bool //publish-all
- Quiet bool //quiet
- ReadOnlyRootfs bool //read-only
- ReadOnlyTmpfs bool //read-only-tmpfs
- Resources CreateResourceConfig
- RestartPolicy string
- Rm bool //rm
- StopSignal syscall.Signal // stop-signal
- StopTimeout uint // stop-timeout
- Sysctl map[string]string //sysctl
- Systemd bool
- Tmpfs []string // tmpfs
- Tty bool //tty
- UsernsMode namespaces.UsernsMode //userns
- User string //user
- UtsMode namespaces.UTSMode //uts
- Mounts []spec.Mount
- MountsFlag []string // mounts
- NamedVolumes []*libpod.ContainerNamedVolume
- Volumes []string //volume
- VolumesFrom []string
- WorkDir string //workdir
LabelOpts []string //SecurityOpts
NoNewPrivs bool //SecurityOpts
ApparmorProfile string //SecurityOpts
SeccompProfilePath string //SecurityOpts
SecurityOpts []string
- Rootfs string
- Syslog bool // Whether to enable syslog on exit commands
+ Privileged bool //privileged
+ ReadOnlyRootfs bool //read-only
+ ReadOnlyTmpfs bool //read-only-tmpfs
+ Sysctl map[string]string //sysctl
+}
+
+// CreateConfig is a pre OCI spec structure. It represents user input from varlink or the CLI
+type CreateConfig struct {
+ Annotations map[string]string
+ Args []string
+ CidFile string
+ ConmonPidFile string
+ Command []string // Full command that will be used
+ UserCommand []string // User-entered command (or image CMD)
+ Detach bool // detach
+ Devices []string // device
+ Entrypoint []string //entrypoint
+ Env map[string]string //env
+ HealthCheck *manifest.Schema2HealthConfig
+ Init bool // init
+ InitPath string //init-path
+ Image string
+ ImageID string
+ BuiltinImgVolumes map[string]struct{} // volumes defined in the image config
+ ImageVolumeType string // how to handle the image volume, either bind, tmpfs, or ignore
+ Interactive bool //interactive
+ Labels map[string]string //label
+ LogDriver string // log-driver
+ LogDriverOpt []string // log-opt
+ Name string //name
+ PodmanPath string
+ Pod string //pod
+ Quiet bool //quiet
+ Resources CreateResourceConfig
+ RestartPolicy string
+ Rm bool //rm
+ StopSignal syscall.Signal // stop-signal
+ StopTimeout uint // stop-timeout
+ Systemd bool
+ Tmpfs []string // tmpfs
+ Tty bool //tty
+ Mounts []spec.Mount
+ MountsFlag []string // mounts
+ NamedVolumes []*libpod.ContainerNamedVolume
+ Volumes []string //volume
+ VolumesFrom []string
+ WorkDir string //workdir
+ Rootfs string
+ Security SecurityConfig
+ Syslog bool // Whether to enable syslog on exit commands
+
+ // Namespaces
+ Pid PidConfig
+ Ipc IpcConfig
+ Cgroup CgroupConfig
+ User UserConfig
+ Uts UtsConfig
+ Network NetworkConfig
}
func u32Ptr(i int64) *uint32 { u := uint32(i); return &u }
@@ -199,7 +234,6 @@ func (c *CreateConfig) createExitCommand(runtime *libpod.Runtime) ([]string, err
// GetContainerCreateOptions takes a CreateConfig and returns a slice of CtrCreateOptions
func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod, mounts []spec.Mount, namedVolumes []*libpod.ContainerNamedVolume) ([]libpod.CtrCreateOption, error) {
var options []libpod.CtrCreateOption
- var portBindings []ocicni.PortMapping
var err error
if c.Interactive {
@@ -216,15 +250,6 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
logrus.Debugf("adding container to pod %s", c.Pod)
options = append(options, runtime.WithPod(pod))
}
- if c.Cgroups == "disabled" {
- options = append(options, libpod.WithNoCgroups())
- }
- if len(c.PortBindings) > 0 {
- portBindings, err = c.CreatePortBindings()
- if err != nil {
- return nil, errors.Wrapf(err, "unable to create port bindings")
- }
- }
if len(mounts) != 0 || len(namedVolumes) != 0 {
destinations := []string{}
@@ -253,187 +278,72 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
// does not have one
options = append(options, libpod.WithEntrypoint(c.Entrypoint))
- networks := make([]string, 0)
- userNetworks := c.NetMode.UserDefined()
- if IsPod(userNetworks) {
- userNetworks = ""
- }
- if userNetworks != "" {
- for _, netName := range strings.Split(userNetworks, ",") {
- if netName == "" {
- return nil, errors.Wrapf(err, "container networks %q invalid", networks)
- }
- networks = append(networks, netName)
- }
- }
-
- if c.NetMode.IsNS() {
- ns := c.NetMode.NS()
- if ns == "" {
- return nil, errors.Errorf("invalid empty user-defined network namespace")
- }
- _, err := os.Stat(ns)
- if err != nil {
- return nil, err
- }
- } else if c.NetMode.IsContainer() {
- connectedCtr, err := runtime.LookupContainer(c.NetMode.Container())
- if err != nil {
- return nil, errors.Wrapf(err, "container %q not found", c.NetMode.Container())
- }
- options = append(options, libpod.WithNetNSFrom(connectedCtr))
- } else if !c.NetMode.IsHost() && !c.NetMode.IsNone() {
- hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
- postConfigureNetNS := hasUserns && !c.UsernsMode.IsHost()
- options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks))
- }
-
- if c.CgroupMode.IsNS() {
- ns := c.CgroupMode.NS()
- if ns == "" {
- return nil, errors.Errorf("invalid empty user-defined network namespace")
- }
- _, err := os.Stat(ns)
- if err != nil {
- return nil, err
- }
- } else if c.CgroupMode.IsContainer() {
- connectedCtr, err := runtime.LookupContainer(c.CgroupMode.Container())
- if err != nil {
- return nil, errors.Wrapf(err, "container %q not found", c.CgroupMode.Container())
- }
- options = append(options, libpod.WithCgroupNSFrom(connectedCtr))
- }
+ // TODO: MNT, USER, CGROUP
+ options = append(options, libpod.WithStopSignal(c.StopSignal))
+ options = append(options, libpod.WithStopTimeout(c.StopTimeout))
- if c.UsernsMode.IsNS() {
- ns := c.UsernsMode.NS()
- if ns == "" {
- return nil, errors.Errorf("invalid empty user-defined user namespace")
- }
- _, err := os.Stat(ns)
- if err != nil {
- return nil, err
- }
- options = append(options, libpod.WithIDMappings(*c.IDMappings))
- } else if c.UsernsMode.IsContainer() {
- connectedCtr, err := runtime.LookupContainer(c.UsernsMode.Container())
- if err != nil {
- return nil, errors.Wrapf(err, "container %q not found", c.UsernsMode.Container())
- }
- options = append(options, libpod.WithUserNSFrom(connectedCtr))
- } else {
- options = append(options, libpod.WithIDMappings(*c.IDMappings))
+ logPath := getLoggingPath(c.LogDriverOpt)
+ if logPath != "" {
+ options = append(options, libpod.WithLogPath(logPath))
}
- if c.PidMode.IsContainer() {
- connectedCtr, err := runtime.LookupContainer(c.PidMode.Container())
- if err != nil {
- return nil, errors.Wrapf(err, "container %q not found", c.PidMode.Container())
- }
-
- options = append(options, libpod.WithPIDNSFrom(connectedCtr))
+ if c.LogDriver != "" {
+ options = append(options, libpod.WithLogDriver(c.LogDriver))
}
- if c.IpcMode.IsContainer() {
- connectedCtr, err := runtime.LookupContainer(c.IpcMode.Container())
- if err != nil {
- return nil, errors.Wrapf(err, "container %q not found", c.IpcMode.Container())
- }
-
- options = append(options, libpod.WithIPCNSFrom(connectedCtr))
+ secOpts, err := c.Security.ToCreateOptions()
+ if err != nil {
+ return nil, err
}
+ options = append(options, secOpts...)
- if IsPod(string(c.UtsMode)) {
- options = append(options, libpod.WithUTSNSFromPod(pod))
+ nsOpts, err := c.Cgroup.ToCreateOptions(runtime)
+ if err != nil {
+ return nil, err
}
- if c.UtsMode.IsContainer() {
- connectedCtr, err := runtime.LookupContainer(c.UtsMode.Container())
- if err != nil {
- return nil, errors.Wrapf(err, "container %q not found", c.UtsMode.Container())
- }
+ options = append(options, nsOpts...)
- options = append(options, libpod.WithUTSNSFrom(connectedCtr))
+ nsOpts, err = c.Ipc.ToCreateOptions(runtime)
+ if err != nil {
+ return nil, err
}
+ options = append(options, nsOpts...)
- // TODO: MNT, USER, CGROUP
- options = append(options, libpod.WithStopSignal(c.StopSignal))
- options = append(options, libpod.WithStopTimeout(c.StopTimeout))
- if len(c.DNSSearch) > 0 {
- options = append(options, libpod.WithDNSSearch(c.DNSSearch))
- }
- if len(c.DNSServers) > 0 {
- if len(c.DNSServers) == 1 && strings.ToLower(c.DNSServers[0]) == "none" {
- options = append(options, libpod.WithUseImageResolvConf())
- } else {
- options = append(options, libpod.WithDNS(c.DNSServers))
- }
- }
- if len(c.DNSOpt) > 0 {
- options = append(options, libpod.WithDNSOption(c.DNSOpt))
- }
- if c.NoHosts {
- options = append(options, libpod.WithUseImageHosts())
- }
- if len(c.HostAdd) > 0 && !c.NoHosts {
- options = append(options, libpod.WithHosts(c.HostAdd))
- }
- logPath := getLoggingPath(c.LogDriverOpt)
- if logPath != "" {
- options = append(options, libpod.WithLogPath(logPath))
+ nsOpts, err = c.Pid.ToCreateOptions(runtime)
+ if err != nil {
+ return nil, err
}
+ options = append(options, nsOpts...)
- if c.LogDriver != "" {
- options = append(options, libpod.WithLogDriver(c.LogDriver))
+ nsOpts, err = c.Network.ToCreateOptions(runtime, &c.User)
+ if err != nil {
+ return nil, err
}
+ options = append(options, nsOpts...)
- if c.IPAddress != "" {
- ip := net.ParseIP(c.IPAddress)
- if ip == nil {
- return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as IP address", c.IPAddress)
- } else if ip.To4() == nil {
- return nil, errors.Wrapf(define.ErrInvalidArg, "%s is not an IPv4 address", c.IPAddress)
- }
- options = append(options, libpod.WithStaticIP(ip))
+ nsOpts, err = c.Uts.ToCreateOptions(runtime, pod)
+ if err != nil {
+ return nil, err
}
+ options = append(options, nsOpts...)
- if c.MacAddress != "" {
- mac, err := net.ParseMAC(c.MacAddress)
- if err != nil {
- return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as MAC address: %v", c.MacAddress, err)
- }
- options = append(options, libpod.WithStaticMAC(mac))
+ nsOpts, err = c.User.ToCreateOptions(runtime)
+ if err != nil {
+ return nil, err
}
-
- options = append(options, libpod.WithPrivileged(c.Privileged))
+ options = append(options, nsOpts...)
useImageVolumes := c.ImageVolumeType == TypeBind
// Gather up the options for NewContainer which consist of With... funcs
options = append(options, libpod.WithRootFSFromImage(c.ImageID, c.Image, useImageVolumes))
- options = append(options, libpod.WithSecLabels(c.LabelOpts))
options = append(options, libpod.WithConmonPidFile(c.ConmonPidFile))
options = append(options, libpod.WithLabels(c.Labels))
- options = append(options, libpod.WithUser(c.User))
- if c.IpcMode.IsHost() {
- options = append(options, libpod.WithShmDir("/dev/shm"))
-
- } else if c.IpcMode.IsContainer() {
- ctr, err := runtime.LookupContainer(c.IpcMode.Container())
- if err != nil {
- return nil, errors.Wrapf(err, "container %q not found", c.IpcMode.Container())
- }
- options = append(options, libpod.WithShmDir(ctr.ShmDir()))
- }
options = append(options, libpod.WithShmSize(c.Resources.ShmSize))
- options = append(options, libpod.WithGroups(c.GroupAdd))
if c.Rootfs != "" {
options = append(options, libpod.WithRootFS(c.Rootfs))
}
// Default used if not overridden on command line
- if c.CgroupParent != "" {
- options = append(options, libpod.WithCgroupParent(c.CgroupParent))
- }
-
if c.RestartPolicy != "" {
if c.RestartPolicy == "unless-stopped" {
return nil, errors.Wrapf(define.ErrInvalidArg, "the unless-stopped restart policy is not supported")
@@ -467,38 +377,6 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
return options, nil
}
-// CreatePortBindings iterates ports mappings and exposed ports into a format CNI understands
-func (c *CreateConfig) CreatePortBindings() ([]ocicni.PortMapping, error) {
- return NatToOCIPortBindings(c.PortBindings)
-}
-
-// NatToOCIPortBindings iterates a nat.portmap slice and creates []ocicni portmapping slice
-func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) {
- var portBindings []ocicni.PortMapping
- for containerPb, hostPb := range ports {
- var pm ocicni.PortMapping
- pm.ContainerPort = int32(containerPb.Int())
- for _, i := range hostPb {
- var hostPort int
- var err error
- pm.HostIP = i.HostIP
- if i.HostPort == "" {
- hostPort = containerPb.Int()
- } else {
- hostPort, err = strconv.Atoi(i.HostPort)
- if err != nil {
- return nil, errors.Wrapf(err, "unable to convert host port to integer")
- }
- }
-
- pm.HostPort = int32(hostPort)
- pm.Protocol = containerPb.Proto()
- portBindings = append(portBindings, pm)
- }
- }
- return portBindings, nil
-}
-
// AddPrivilegedDevices iterates through host devices and adds all
// host devices to the spec
func (c *CreateConfig) AddPrivilegedDevices(g *generate.Generator) error {
diff --git a/pkg/spec/namespaces.go b/pkg/spec/namespaces.go
new file mode 100644
index 000000000..a45137416
--- /dev/null
+++ b/pkg/spec/namespaces.go
@@ -0,0 +1,433 @@
+package createconfig
+
+import (
+ "net"
+ "os"
+ "strconv"
+ "strings"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/cgroups"
+ "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/docker/go-connections/nat"
+ spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/opencontainers/runtime-tools/generate"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserConfig) ([]libpod.CtrCreateOption, error) {
+ var portBindings []ocicni.PortMapping
+ var err error
+ if len(c.PortBindings) > 0 {
+ portBindings, err = NatToOCIPortBindings(c.PortBindings)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to create port bindings")
+ }
+ }
+
+ options := make([]libpod.CtrCreateOption, 0)
+ userNetworks := c.NetMode.UserDefined()
+ networks := make([]string, 0)
+
+ if IsPod(userNetworks) {
+ userNetworks = ""
+ }
+ if userNetworks != "" {
+ for _, netName := range strings.Split(userNetworks, ",") {
+ if netName == "" {
+ return nil, errors.Errorf("container networks %q invalid", userNetworks)
+ }
+ networks = append(networks, netName)
+ }
+ }
+
+ if c.NetMode.IsNS() {
+ ns := c.NetMode.NS()
+ if ns == "" {
+ return nil, errors.Errorf("invalid empty user-defined network namespace")
+ }
+ _, err := os.Stat(ns)
+ if err != nil {
+ return nil, err
+ }
+ } else if c.NetMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.NetMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.NetMode.Container())
+ }
+ options = append(options, libpod.WithNetNSFrom(connectedCtr))
+ } else if !c.NetMode.IsHost() && !c.NetMode.IsNone() {
+ postConfigureNetNS := userns.getPostConfigureNetNS()
+ options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks))
+ }
+
+ if len(c.DNSSearch) > 0 {
+ options = append(options, libpod.WithDNSSearch(c.DNSSearch))
+ }
+ if len(c.DNSServers) > 0 {
+ if len(c.DNSServers) == 1 && strings.ToLower(c.DNSServers[0]) == "none" {
+ options = append(options, libpod.WithUseImageResolvConf())
+ } else {
+ options = append(options, libpod.WithDNS(c.DNSServers))
+ }
+ }
+ if len(c.DNSOpt) > 0 {
+ options = append(options, libpod.WithDNSOption(c.DNSOpt))
+ }
+ if c.IPAddress != "" {
+ ip := net.ParseIP(c.IPAddress)
+ if ip == nil {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as IP address", c.IPAddress)
+ } else if ip.To4() == nil {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "%s is not an IPv4 address", c.IPAddress)
+ }
+ options = append(options, libpod.WithStaticIP(ip))
+ }
+
+ if c.MacAddress != "" {
+ mac, err := net.ParseMAC(c.MacAddress)
+ if err != nil {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as MAC address: %v", c.MacAddress, err)
+ }
+ options = append(options, libpod.WithStaticMAC(mac))
+ }
+
+ return options, nil
+}
+
+func (c *NetworkConfig) ConfigureGenerator(g *generate.Generator) error {
+ netMode := c.NetMode
+ if netMode.IsHost() {
+ logrus.Debug("Using host netmode")
+ if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil {
+ return err
+ }
+ } else if netMode.IsNone() {
+ logrus.Debug("Using none netmode")
+ } else if netMode.IsBridge() {
+ logrus.Debug("Using bridge netmode")
+ } else if netCtr := netMode.Container(); netCtr != "" {
+ logrus.Debugf("using container %s netmode", netCtr)
+ } else if IsNS(string(netMode)) {
+ logrus.Debug("Using ns netmode")
+ if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), NS(string(netMode))); err != nil {
+ return err
+ }
+ } else if IsPod(string(netMode)) {
+ logrus.Debug("Using pod netmode, unless pod is not sharing")
+ } else if netMode.IsSlirp4netns() {
+ logrus.Debug("Using slirp4netns netmode")
+ } else if netMode.IsUserDefined() {
+ logrus.Debug("Using user defined netmode")
+ } else {
+ return errors.Errorf("unknown network mode")
+ }
+
+ if c.HTTPProxy {
+ for _, envSpec := range []string{
+ "http_proxy",
+ "HTTP_PROXY",
+ "https_proxy",
+ "HTTPS_PROXY",
+ "ftp_proxy",
+ "FTP_PROXY",
+ "no_proxy",
+ "NO_PROXY",
+ } {
+ envVal := os.Getenv(envSpec)
+ if envVal != "" {
+ g.AddProcessEnv(envSpec, envVal)
+ }
+ }
+ }
+
+ if g.Config.Annotations == nil {
+ g.Config.Annotations = make(map[string]string)
+ }
+
+ if c.PublishAll {
+ g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue
+ } else {
+ g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse
+ }
+
+ return nil
+}
+
+// NatToOCIPortBindings iterates a nat.portmap slice and creates []ocicni portmapping slice
+func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) {
+ var portBindings []ocicni.PortMapping
+ for containerPb, hostPb := range ports {
+ var pm ocicni.PortMapping
+ pm.ContainerPort = int32(containerPb.Int())
+ for _, i := range hostPb {
+ var hostPort int
+ var err error
+ pm.HostIP = i.HostIP
+ if i.HostPort == "" {
+ hostPort = containerPb.Int()
+ } else {
+ hostPort, err = strconv.Atoi(i.HostPort)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to convert host port to integer")
+ }
+ }
+
+ pm.HostPort = int32(hostPort)
+ pm.Protocol = containerPb.Proto()
+ portBindings = append(portBindings, pm)
+ }
+ }
+ return portBindings, nil
+}
+
+func (c *CgroupConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ if c.CgroupMode.IsNS() {
+ ns := c.CgroupMode.NS()
+ if ns == "" {
+ return nil, errors.Errorf("invalid empty user-defined network namespace")
+ }
+ _, err := os.Stat(ns)
+ if err != nil {
+ return nil, err
+ }
+ } else if c.CgroupMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.CgroupMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.CgroupMode.Container())
+ }
+ options = append(options, libpod.WithCgroupNSFrom(connectedCtr))
+ }
+
+ if c.CgroupParent != "" {
+ options = append(options, libpod.WithCgroupParent(c.CgroupParent))
+ }
+
+ if c.Cgroups == "disabled" {
+ options = append(options, libpod.WithNoCgroups())
+ }
+
+ return options, nil
+}
+
+func (c *UserConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ if c.UsernsMode.IsNS() {
+ ns := c.UsernsMode.NS()
+ if ns == "" {
+ return nil, errors.Errorf("invalid empty user-defined user namespace")
+ }
+ _, err := os.Stat(ns)
+ if err != nil {
+ return nil, err
+ }
+ options = append(options, libpod.WithIDMappings(*c.IDMappings))
+ } else if c.UsernsMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.UsernsMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.UsernsMode.Container())
+ }
+ options = append(options, libpod.WithUserNSFrom(connectedCtr))
+ } else {
+ options = append(options, libpod.WithIDMappings(*c.IDMappings))
+ }
+
+ options = append(options, libpod.WithUser(c.User))
+ options = append(options, libpod.WithGroups(c.GroupAdd))
+
+ return options, nil
+}
+
+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 {
+ return err
+ }
+ // runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping
+ g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1))
+ g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1))
+ }
+
+ if (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost() {
+ if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
+ return err
+ }
+ }
+ for _, uidmap := range c.IDMappings.UIDMap {
+ g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
+ }
+ for _, gidmap := range c.IDMappings.GIDMap {
+ g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
+ }
+ return nil
+}
+
+func (c *UserConfig) getPostConfigureNetNS() bool {
+ hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
+ postConfigureNetNS := hasUserns && !c.UsernsMode.IsHost()
+ return postConfigureNetNS
+}
+
+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())
+}
+
+func (c *IpcConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ if c.IpcMode.IsHost() {
+ options = append(options, libpod.WithShmDir("/dev/shm"))
+ } else if c.IpcMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.IpcMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.IpcMode.Container())
+ }
+
+ options = append(options, libpod.WithIPCNSFrom(connectedCtr))
+ options = append(options, libpod.WithShmDir(connectedCtr.ShmDir()))
+ }
+
+ return options, nil
+}
+
+func (c *IpcConfig) ConfigureGenerator(g *generate.Generator) error {
+ ipcMode := c.IpcMode
+ if IsNS(string(ipcMode)) {
+ return g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), NS(string(ipcMode)))
+ }
+ if ipcMode.IsHost() {
+ return g.RemoveLinuxNamespace(string(spec.IPCNamespace))
+ }
+ if ipcCtr := ipcMode.Container(); ipcCtr != "" {
+ logrus.Debugf("Using container %s ipcmode", ipcCtr)
+ }
+
+ return nil
+}
+
+func (c *CgroupConfig) ConfigureGenerator(g *generate.Generator) error {
+ cgroupMode := c.CgroupMode
+ if cgroupMode.IsDefaultValue() {
+ // If the value is not specified, default to "private" on cgroups v2 and "host" on cgroups v1.
+ unified, err := cgroups.IsCgroup2UnifiedMode()
+ if err != nil {
+ return err
+ }
+ if unified {
+ cgroupMode = "private"
+ } else {
+ cgroupMode = "host"
+ }
+ }
+ if cgroupMode.IsNS() {
+ return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), NS(string(cgroupMode)))
+ }
+ if cgroupMode.IsHost() {
+ return g.RemoveLinuxNamespace(string(spec.CgroupNamespace))
+ }
+ if cgroupMode.IsPrivate() {
+ return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "")
+ }
+ if cgCtr := cgroupMode.Container(); cgCtr != "" {
+ logrus.Debugf("Using container %s cgroup mode", cgCtr)
+ }
+ return nil
+}
+
+func (c *PidConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ if c.PidMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.PidMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.PidMode.Container())
+ }
+
+ options = append(options, libpod.WithPIDNSFrom(connectedCtr))
+ }
+
+ return options, nil
+}
+
+func (c *PidConfig) ConfigureGenerator(g *generate.Generator) error {
+ pidMode := c.PidMode
+ if IsNS(string(pidMode)) {
+ return g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), NS(string(pidMode)))
+ }
+ if pidMode.IsHost() {
+ return g.RemoveLinuxNamespace(string(spec.PIDNamespace))
+ }
+ if pidCtr := pidMode.Container(); pidCtr != "" {
+ logrus.Debugf("using container %s pidmode", pidCtr)
+ }
+ if IsPod(string(pidMode)) {
+ logrus.Debug("using pod pidmode")
+ }
+ return nil
+}
+
+func (c *UtsConfig) ToCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod) ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ if IsPod(string(c.UtsMode)) {
+ options = append(options, libpod.WithUTSNSFromPod(pod))
+ }
+ if c.UtsMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.UtsMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.UtsMode.Container())
+ }
+
+ options = append(options, libpod.WithUTSNSFrom(connectedCtr))
+ }
+ if c.NoHosts {
+ options = append(options, libpod.WithUseImageHosts())
+ }
+ if len(c.HostAdd) > 0 && !c.NoHosts {
+ options = append(options, libpod.WithHosts(c.HostAdd))
+ }
+
+ return options, nil
+}
+
+func (c *UtsConfig) ConfigureGenerator(g *generate.Generator, net *NetworkConfig, runtime *libpod.Runtime) error {
+ hostname := c.Hostname
+ var err error
+ if hostname == "" {
+ if utsCtrID := c.UtsMode.Container(); utsCtrID != "" {
+ utsCtr, err := runtime.GetContainer(utsCtrID)
+ if err != nil {
+ return errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", utsCtrID)
+ }
+ hostname = utsCtr.Hostname()
+ } else if net.NetMode.IsHost() || c.UtsMode.IsHost() {
+ hostname, err = os.Hostname()
+ if err != nil {
+ return errors.Wrap(err, "unable to retrieve hostname of the host")
+ }
+ } else {
+ logrus.Debug("No hostname set; container's hostname will default to runtime default")
+ }
+ }
+ g.RemoveHostname()
+ if c.Hostname != "" || !c.UtsMode.IsHost() {
+ // Set the hostname in the OCI configuration only
+ // if specified by the user or if we are creating
+ // a new UTS namespace.
+ g.SetHostname(hostname)
+ }
+ g.AddProcessEnv("HOSTNAME", hostname)
+
+ utsMode := c.UtsMode
+ if IsNS(string(utsMode)) {
+ return g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), NS(string(utsMode)))
+ }
+ if utsMode.IsHost() {
+ return g.RemoveLinuxNamespace(string(spec.UTSNamespace))
+ }
+ if utsCtr := utsMode.Container(); utsCtr != "" {
+ logrus.Debugf("using container %s utsmode", utsCtr)
+ }
+ return nil
+}
diff --git a/pkg/spec/security.go b/pkg/spec/security.go
new file mode 100644
index 000000000..05ed94e66
--- /dev/null
+++ b/pkg/spec/security.go
@@ -0,0 +1,172 @@
+package createconfig
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/docker/docker/oci/caps"
+ "github.com/opencontainers/runtime-tools/generate"
+ "github.com/opencontainers/selinux/go-selinux/label"
+ "github.com/pkg/errors"
+)
+
+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
+}
+
+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
+}
+
+func (c *SecurityConfig) SetSecurityOpts(runtime *libpod.Runtime, securityOpts []string) error {
+ for _, opt := range securityOpts {
+ if opt == "no-new-privileges" {
+ c.NoNewPrivs = true
+ } else {
+ con := strings.SplitN(opt, "=", 2)
+ if len(con) != 2 {
+ return fmt.Errorf("invalid --security-opt 1: %q", opt)
+ }
+
+ switch con[0] {
+ case "label":
+ c.LabelOpts = append(c.LabelOpts, con[1])
+ case "apparmor":
+ c.ApparmorProfile = con[1]
+ case "seccomp":
+ c.SeccompProfilePath = con[1]
+ default:
+ return fmt.Errorf("invalid --security-opt 2: %q", opt)
+ }
+ }
+ }
+
+ if c.SeccompProfilePath == "" {
+ var err error
+ c.SeccompProfilePath, err = libpod.DefaultSeccompPath()
+ if err != nil {
+ return err
+ }
+ }
+ c.SecurityOpts = securityOpts
+ return nil
+}
+
+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 caplist []string
+ bounding := configSpec.Process.Capabilities.Bounding
+ if useNotRoot(user.User) {
+ configSpec.Process.Capabilities.Bounding = caplist
+ }
+ caplist, err = caps.TweakCapabilities(configSpec.Process.Capabilities.Bounding, c.CapAdd, c.CapDrop, nil, false)
+ if err != nil {
+ return err
+ }
+
+ configSpec.Process.Capabilities.Bounding = caplist
+ configSpec.Process.Capabilities.Permitted = caplist
+ configSpec.Process.Capabilities.Inheritable = caplist
+ configSpec.Process.Capabilities.Effective = caplist
+ configSpec.Process.Capabilities.Ambient = caplist
+ if useNotRoot(user.User) {
+ caplist, err = caps.TweakCapabilities(bounding, c.CapAdd, c.CapDrop, nil, false)
+ if err != nil {
+ return err
+ }
+ }
+ configSpec.Process.Capabilities.Bounding = caplist
+
+ // 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/spec/spec.go b/pkg/spec/spec.go
index 33e9ec076..7a220012f 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -1,7 +1,6 @@
package createconfig
import (
- "os"
"strings"
"github.com/containers/libpod/libpod"
@@ -10,13 +9,11 @@ import (
"github.com/containers/libpod/pkg/cgroups"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/sysinfo"
- "github.com/docker/docker/oci/caps"
"github.com/docker/go-units"
"github.com/opencontainers/runc/libcontainer/user"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
- "github.com/sirupsen/logrus"
)
const cpuPeriod = 100000
@@ -47,14 +44,13 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
canMountSys := true
isRootless := rootless.IsRootless()
- hasUserns := config.UsernsMode.IsContainer() || config.UsernsMode.IsNS() || len(config.IDMappings.UIDMap) > 0 || len(config.IDMappings.GIDMap) > 0
- inUserNS := isRootless || (hasUserns && !config.UsernsMode.IsHost())
+ inUserNS := config.User.InNS(isRootless)
- if inUserNS && config.NetMode.IsHost() {
+ if inUserNS && config.Network.NetMode.IsHost() {
canMountSys = false
}
- if config.Privileged && canMountSys {
+ if config.Security.Privileged && canMountSys {
cgroupPerm = "rw"
g.RemoveMount("/sys")
sysMnt := spec.Mount{
@@ -68,7 +64,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
addCgroup = false
g.RemoveMount("/sys")
r := "ro"
- if config.Privileged {
+ if config.Security.Privileged {
r = "rw"
}
sysMnt := spec.Mount{
@@ -78,7 +74,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"},
}
g.AddMount(sysMnt)
- if !config.Privileged && isRootless {
+ if !config.Security.Privileged && isRootless {
g.AddLinuxMaskedPaths("/sys/kernel")
}
}
@@ -92,9 +88,9 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
}
// When using a different user namespace, check that the GID 5 is mapped inside
// the container.
- if gid5Available && len(config.IDMappings.GIDMap) > 0 {
+ if gid5Available && len(config.User.IDMappings.GIDMap) > 0 {
mappingFound := false
- for _, r := range config.IDMappings.GIDMap {
+ for _, r := range config.User.IDMappings.GIDMap {
if r.ContainerID <= 5 && 5 < r.ContainerID+r.Size {
mappingFound = true
break
@@ -117,7 +113,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
g.AddMount(devPts)
}
- if inUserNS && config.IpcMode.IsHost() {
+ if inUserNS && config.Ipc.IpcMode.IsHost() {
g.RemoveMount("/dev/mqueue")
devMqueue := spec.Mount{
Destination: "/dev/mqueue",
@@ -127,7 +123,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
}
g.AddMount(devMqueue)
}
- if inUserNS && config.PidMode.IsHost() {
+ if inUserNS && config.Pid.PidMode.IsHost() {
g.RemoveMount("/proc")
procMount := spec.Mount{
Destination: "/proc",
@@ -154,55 +150,6 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
for key, val := range config.Annotations {
g.AddAnnotation(key, val)
}
- g.SetRootReadonly(config.ReadOnlyRootfs)
-
- if config.HTTPProxy {
- for _, envSpec := range []string{
- "http_proxy",
- "HTTP_PROXY",
- "https_proxy",
- "HTTPS_PROXY",
- "ftp_proxy",
- "FTP_PROXY",
- "no_proxy",
- "NO_PROXY",
- } {
- envVal := os.Getenv(envSpec)
- if envVal != "" {
- g.AddProcessEnv(envSpec, envVal)
- }
- }
- }
-
- hostname := config.Hostname
- if hostname == "" {
- if utsCtrID := config.UtsMode.Container(); utsCtrID != "" {
- utsCtr, err := runtime.GetContainer(utsCtrID)
- if err != nil {
- return nil, errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", utsCtrID)
- }
- hostname = utsCtr.Hostname()
- } else if config.NetMode.IsHost() || config.UtsMode.IsHost() {
- hostname, err = os.Hostname()
- if err != nil {
- return nil, errors.Wrap(err, "unable to retrieve hostname of the host")
- }
- } else {
- logrus.Debug("No hostname set; container's hostname will default to runtime default")
- }
- }
- g.RemoveHostname()
- if config.Hostname != "" || !config.UtsMode.IsHost() {
- // Set the hostname in the OCI configuration only
- // if specified by the user or if we are creating
- // a new UTS namespace.
- g.SetHostname(hostname)
- }
- g.AddProcessEnv("HOSTNAME", hostname)
-
- for sysctlKey, sysctlVal := range config.Sysctl {
- g.AddLinuxSysctl(sysctlKey, sysctlVal)
- }
g.AddProcessEnv("container", "podman")
addedResources := false
@@ -272,7 +219,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
}
// Devices
- if config.Privileged {
+ if config.Security.Privileged {
// If privileged, we need to add all the host devices to the
// spec. We do not add the user provided ones because we are
// already adding them all.
@@ -287,17 +234,11 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
}
}
- for _, uidmap := range config.IDMappings.UIDMap {
- g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
- }
- for _, gidmap := range config.IDMappings.GIDMap {
- g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
- }
// SECURITY OPTS
- g.SetProcessNoNewPrivileges(config.NoNewPrivs)
+ g.SetProcessNoNewPrivileges(config.Security.NoNewPrivs)
- if !config.Privileged {
- g.SetProcessApparmorProfile(config.ApparmorProfile)
+ if !config.Security.Privileged {
+ g.SetProcessApparmorProfile(config.Security.ApparmorProfile)
}
blockAccessToKernelFilesystems(config, &g)
@@ -341,54 +282,35 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
return nil, err
}
- if err := addPidNS(config, &g); err != nil {
+ // NAMESPACES
+
+ if err := config.Pid.ConfigureGenerator(&g); err != nil {
return nil, err
}
- if err := addUserNS(config, &g); err != nil {
+ if err := config.User.ConfigureGenerator(&g); err != nil {
return nil, err
}
- if err := addNetNS(config, &g); err != nil {
+ if err := config.Network.ConfigureGenerator(&g); err != nil {
return nil, err
}
- if err := addUTSNS(config, &g); err != nil {
+ if err := config.Uts.ConfigureGenerator(&g, &config.Network, runtime); err != nil {
return nil, err
}
- if err := addIpcNS(config, &g); err != nil {
+ if err := config.Ipc.ConfigureGenerator(&g); err != nil {
return nil, err
}
- if err := addCgroupNS(config, &g); err != nil {
+ if err := config.Cgroup.ConfigureGenerator(&g); err != nil {
return nil, err
}
configSpec := g.Config
- // HANDLE CAPABILITIES
- // NOTE: Must happen before SECCOMP
- if !config.Privileged {
- if err := setupCapabilities(config, configSpec); err != nil {
- return nil, err
- }
- } else {
- g.SetupPrivileged(true)
- }
-
- // HANDLE SECCOMP
-
- if config.SeccompProfilePath != "unconfined" {
- seccompConfig, err := getSeccompConfig(config, configSpec)
- if err != nil {
- return nil, err
- }
- configSpec.Linux.Seccomp = seccompConfig
- }
-
- // Clear default Seccomp profile from Generator for privileged containers
- if config.SeccompProfilePath == "unconfined" || config.Privileged {
- configSpec.Linux.Seccomp = nil
+ if err := config.Security.ConfigureGenerator(&g, &config.User); err != nil {
+ return nil, err
}
// BIND MOUNTS
@@ -430,7 +352,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
}
}
- switch config.Cgroups {
+ switch config.Cgroup.Cgroups {
case "disabled":
if addedResources {
return nil, errors.New("cannot specify resource limits when cgroups are disabled is specified")
@@ -461,48 +383,23 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
configSpec.Annotations[libpod.InspectAnnotationVolumesFrom] = strings.Join(config.VolumesFrom, ",")
}
- if config.Privileged {
+ if config.Security.Privileged {
configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseTrue
} else {
configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseFalse
}
- if config.PublishAll {
- configSpec.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue
- } else {
- configSpec.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse
- }
-
if config.Init {
configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseTrue
} else {
configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseFalse
}
- for _, opt := range config.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]
- }
- }
-
return configSpec, nil
}
func blockAccessToKernelFilesystems(config *CreateConfig, g *generate.Generator) {
- if !config.Privileged {
+ if !config.Security.Privileged {
for _, mp := range []string{
"/proc/acpi",
"/proc/kcore",
@@ -518,7 +415,7 @@ func blockAccessToKernelFilesystems(config *CreateConfig, g *generate.Generator)
g.AddLinuxMaskedPaths(mp)
}
- if config.PidMode.IsHost() && rootless.IsRootless() {
+ if config.Pid.PidMode.IsHost() && rootless.IsRootless() {
return
}
@@ -535,130 +432,6 @@ func blockAccessToKernelFilesystems(config *CreateConfig, g *generate.Generator)
}
}
-func addPidNS(config *CreateConfig, g *generate.Generator) error {
- pidMode := config.PidMode
- if IsNS(string(pidMode)) {
- return g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), NS(string(pidMode)))
- }
- if pidMode.IsHost() {
- return g.RemoveLinuxNamespace(string(spec.PIDNamespace))
- }
- if pidCtr := pidMode.Container(); pidCtr != "" {
- logrus.Debugf("using container %s pidmode", pidCtr)
- }
- if IsPod(string(pidMode)) {
- logrus.Debug("using pod pidmode")
- }
- return nil
-}
-
-func addUserNS(config *CreateConfig, g *generate.Generator) error {
- if IsNS(string(config.UsernsMode)) {
- if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), NS(string(config.UsernsMode))); err != nil {
- return err
- }
- // runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping
- g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1))
- g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1))
- }
-
- if (len(config.IDMappings.UIDMap) > 0 || len(config.IDMappings.GIDMap) > 0) && !config.UsernsMode.IsHost() {
- if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
- return err
- }
- }
- return nil
-}
-
-func addNetNS(config *CreateConfig, g *generate.Generator) error {
- netMode := config.NetMode
- if netMode.IsHost() {
- logrus.Debug("Using host netmode")
- return g.RemoveLinuxNamespace(string(spec.NetworkNamespace))
- } else if netMode.IsNone() {
- logrus.Debug("Using none netmode")
- return nil
- } else if netMode.IsBridge() {
- logrus.Debug("Using bridge netmode")
- return nil
- } else if netCtr := netMode.Container(); netCtr != "" {
- logrus.Debugf("using container %s netmode", netCtr)
- return nil
- } else if IsNS(string(netMode)) {
- logrus.Debug("Using ns netmode")
- return g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), NS(string(netMode)))
- } else if IsPod(string(netMode)) {
- logrus.Debug("Using pod netmode, unless pod is not sharing")
- return nil
- } else if netMode.IsSlirp4netns() {
- logrus.Debug("Using slirp4netns netmode")
- return nil
- } else if netMode.IsUserDefined() {
- logrus.Debug("Using user defined netmode")
- return nil
- }
- return errors.Errorf("unknown network mode")
-}
-
-func addUTSNS(config *CreateConfig, g *generate.Generator) error {
- utsMode := config.UtsMode
- if IsNS(string(utsMode)) {
- return g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), NS(string(utsMode)))
- }
- if utsMode.IsHost() {
- return g.RemoveLinuxNamespace(string(spec.UTSNamespace))
- }
- if utsCtr := utsMode.Container(); utsCtr != "" {
- logrus.Debugf("using container %s utsmode", utsCtr)
- }
- return nil
-}
-
-func addIpcNS(config *CreateConfig, g *generate.Generator) error {
- ipcMode := config.IpcMode
- if IsNS(string(ipcMode)) {
- return g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), NS(string(ipcMode)))
- }
- if ipcMode.IsHost() {
- return g.RemoveLinuxNamespace(string(spec.IPCNamespace))
- }
- if ipcCtr := ipcMode.Container(); ipcCtr != "" {
- logrus.Debugf("Using container %s ipcmode", ipcCtr)
- }
-
- return nil
-}
-
-func addCgroupNS(config *CreateConfig, g *generate.Generator) error {
- cgroupMode := config.CgroupMode
-
- if cgroupMode.IsDefaultValue() {
- // If the value is not specified, default to "private" on cgroups v2 and "host" on cgroups v1.
- unified, err := cgroups.IsCgroup2UnifiedMode()
- if err != nil {
- return err
- }
- if unified {
- cgroupMode = "private"
- } else {
- cgroupMode = "host"
- }
- }
- if cgroupMode.IsNS() {
- return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), NS(string(cgroupMode)))
- }
- if cgroupMode.IsHost() {
- return g.RemoveLinuxNamespace(string(spec.CgroupNamespace))
- }
- if cgroupMode.IsPrivate() {
- return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "")
- }
- if cgCtr := cgroupMode.Container(); cgCtr != "" {
- logrus.Debugf("Using container %s cgroup mode", cgCtr)
- }
- return nil
-}
-
func addRlimits(config *CreateConfig, g *generate.Generator) error {
var (
kernelMax uint64 = 1048576
@@ -702,37 +475,3 @@ func addRlimits(config *CreateConfig, g *generate.Generator) error {
return nil
}
-
-func setupCapabilities(config *CreateConfig, configSpec *spec.Spec) error {
- useNotRoot := func(user string) bool {
- if user == "" || user == "root" || user == "0" {
- return false
- }
- return true
- }
-
- var err error
- var caplist []string
- bounding := configSpec.Process.Capabilities.Bounding
- if useNotRoot(config.User) {
- configSpec.Process.Capabilities.Bounding = caplist
- }
- caplist, err = caps.TweakCapabilities(configSpec.Process.Capabilities.Bounding, config.CapAdd, config.CapDrop, nil, false)
- if err != nil {
- return err
- }
-
- configSpec.Process.Capabilities.Bounding = caplist
- configSpec.Process.Capabilities.Permitted = caplist
- configSpec.Process.Capabilities.Inheritable = caplist
- configSpec.Process.Capabilities.Effective = caplist
- configSpec.Process.Capabilities.Ambient = caplist
- if useNotRoot(config.User) {
- caplist, err = caps.TweakCapabilities(bounding, config.CapAdd, config.CapDrop, nil, false)
- if err != nil {
- return err
- }
- }
- configSpec.Process.Capabilities.Bounding = caplist
- return nil
-}
diff --git a/pkg/spec/spec_test.go b/pkg/spec/spec_test.go
index 2f91e1b21..0f63b2bbc 100644
--- a/pkg/spec/spec_test.go
+++ b/pkg/spec/spec_test.go
@@ -21,9 +21,9 @@ var (
func makeTestCreateConfig() *CreateConfig {
cc := new(CreateConfig)
cc.Resources = CreateResourceConfig{}
- cc.IDMappings = new(storage.IDMappingOptions)
- cc.IDMappings.UIDMap = []idtools.IDMap{}
- cc.IDMappings.GIDMap = []idtools.IDMap{}
+ cc.User.IDMappings = new(storage.IDMappingOptions)
+ cc.User.IDMappings.UIDMap = []idtools.IDMap{}
+ cc.User.IDMappings.GIDMap = []idtools.IDMap{}
return cc
}
diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go
index e30bdfc67..dbdab0030 100644
--- a/pkg/spec/storage.go
+++ b/pkg/spec/storage.go
@@ -136,9 +136,9 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount,
unifiedMounts[initMount.Destination] = initMount
}
- // Before superceding, we need to find volume mounts which conflict with
+ // Before superseding, we need to find volume mounts which conflict with
// named volumes, and vice versa.
- // We'll delete the conflicts here as we supercede.
+ // We'll delete the conflicts here as we supersede.
for dest := range unifiedMounts {
if _, ok := baseVolumes[dest]; ok {
delete(baseVolumes, dest)
@@ -150,7 +150,7 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount,
}
}
- // Supercede volumes-from/image volumes with unified volumes from above.
+ // Supersede volumes-from/image volumes with unified volumes from above.
// This is an unconditional replacement.
for dest, mount := range unifiedMounts {
baseMounts[dest] = mount
@@ -160,7 +160,7 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount,
}
// If requested, add tmpfs filesystems for read-only containers.
- if config.ReadOnlyRootfs && config.ReadOnlyTmpfs {
+ if config.Security.ReadOnlyRootfs && config.Security.ReadOnlyTmpfs {
readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"}
options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"}
for _, dest := range readonlyTmpfs {
@@ -336,7 +336,7 @@ func (config *CreateConfig) getMounts() (map[string]spec.Mount, map[string]*libp
// 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 suppored options.
+ // precise errors regarding supported format versus supported options.
for _, mount := range config.MountsFlag {
arr := strings.SplitN(mount, ",", 2)
if len(arr) < 2 {
@@ -807,7 +807,7 @@ func (config *CreateConfig) addContainerInitBinary(path string) (spec.Mount, err
if path == "" {
return mount, fmt.Errorf("please specify a path to the container-init binary")
}
- if !config.PidMode.IsPrivate() {
+ if !config.Pid.PidMode.IsPrivate() {
return mount, fmt.Errorf("cannot add init binary as PID 1 (PID namespace isn't private)")
}
if config.Systemd {
@@ -820,7 +820,7 @@ func (config *CreateConfig) addContainerInitBinary(path string) (spec.Mount, err
return mount, nil
}
-// Supercede existing mounts in the spec with new, user-specified mounts.
+// 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?
diff --git a/pkg/timetype/timestamp.go b/pkg/timetype/timestamp.go
new file mode 100644
index 000000000..eb904a574
--- /dev/null
+++ b/pkg/timetype/timestamp.go
@@ -0,0 +1,131 @@
+package timetype
+
+// code adapted from https://github.com/moby/moby/blob/master/api/types/time/timestamp.go
+
+import (
+ "fmt"
+ "math"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// These are additional predefined layouts for use in Time.Format and Time.Parse
+// with --since and --until parameters for `docker logs` and `docker events`
+const (
+ rFC3339Local = "2006-01-02T15:04:05" // RFC3339 with local timezone
+ rFC3339NanoLocal = "2006-01-02T15:04:05.999999999" // RFC3339Nano with local timezone
+ dateWithZone = "2006-01-02Z07:00" // RFC3339 with time at 00:00:00
+ dateLocal = "2006-01-02" // RFC3339 with local timezone and time at 00:00:00
+)
+
+// GetTimestamp tries to parse given string as golang duration,
+// then RFC3339 time and finally as a Unix timestamp. If
+// any of these were successful, it returns a Unix timestamp
+// as string otherwise returns the given value back.
+// In case of duration input, the returned timestamp is computed
+// as the given reference time minus the amount of the duration.
+func GetTimestamp(value string, reference time.Time) (string, error) {
+ if d, err := time.ParseDuration(value); value != "0" && err == nil {
+ return strconv.FormatInt(reference.Add(-d).Unix(), 10), nil
+ }
+
+ var format string
+ // if the string has a Z or a + or three dashes use parse otherwise use parseinlocation
+ parseInLocation := !(strings.ContainsAny(value, "zZ+") || strings.Count(value, "-") == 3)
+
+ if strings.Contains(value, ".") {
+ if parseInLocation {
+ format = rFC3339NanoLocal
+ } else {
+ format = time.RFC3339Nano
+ }
+ } else if strings.Contains(value, "T") {
+ // we want the number of colons in the T portion of the timestamp
+ tcolons := strings.Count(value, ":")
+ // if parseInLocation is off and we have a +/- zone offset (not Z) then
+ // there will be an extra colon in the input for the tz offset subtract that
+ // colon from the tcolons count
+ if !parseInLocation && !strings.ContainsAny(value, "zZ") && tcolons > 0 {
+ tcolons--
+ }
+ if parseInLocation {
+ switch tcolons {
+ case 0:
+ format = "2006-01-02T15"
+ case 1:
+ format = "2006-01-02T15:04"
+ default:
+ format = rFC3339Local
+ }
+ } else {
+ switch tcolons {
+ case 0:
+ format = "2006-01-02T15Z07:00"
+ case 1:
+ format = "2006-01-02T15:04Z07:00"
+ default:
+ format = time.RFC3339
+ }
+ }
+ } else if parseInLocation {
+ format = dateLocal
+ } else {
+ format = dateWithZone
+ }
+
+ var t time.Time
+ var err error
+
+ if parseInLocation {
+ t, err = time.ParseInLocation(format, value, time.FixedZone(reference.Zone()))
+ } else {
+ t, err = time.Parse(format, value)
+ }
+
+ if err != nil {
+ // if there is a `-` then it's an RFC3339 like timestamp
+ if strings.Contains(value, "-") {
+ return "", err // was probably an RFC3339 like timestamp but the parser failed with an error
+ }
+ if _, _, err := parseTimestamp(value); err != nil {
+ return "", fmt.Errorf("failed to parse value as time or duration: %q", value)
+ }
+ return value, nil // unix timestamp in and out case (meaning: the value passed at the command line is already in the right format for passing to the server)
+ }
+
+ return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond())), nil
+}
+
+// ParseTimestamps returns seconds and nanoseconds from a timestamp that has the
+// format "%d.%09d", time.Unix(), int64(time.Nanosecond()))
+// if the incoming nanosecond portion is longer or shorter than 9 digits it is
+// converted to nanoseconds. The expectation is that the seconds and
+// seconds will be used to create a time variable. For example:
+// seconds, nanoseconds, err := ParseTimestamp("1136073600.000000001",0)
+// if err == nil since := time.Unix(seconds, nanoseconds)
+// returns seconds as def(aultSeconds) if value == ""
+func ParseTimestamps(value string, def int64) (int64, int64, error) {
+ if value == "" {
+ return def, 0, nil
+ }
+ return parseTimestamp(value)
+}
+
+func parseTimestamp(value string) (int64, int64, error) {
+ sa := strings.SplitN(value, ".", 2)
+ s, err := strconv.ParseInt(sa[0], 10, 64)
+ if err != nil {
+ return s, 0, err
+ }
+ if len(sa) != 2 {
+ return s, 0, nil
+ }
+ n, err := strconv.ParseInt(sa[1], 10, 64)
+ if err != nil {
+ return s, n, err
+ }
+ // should already be in nanoseconds but just in case convert n to nanoseconds
+ n = int64(float64(n) * math.Pow(float64(10), float64(9-len(sa[1]))))
+ return s, n, nil
+}
diff --git a/pkg/timetype/timestamp_test.go b/pkg/timetype/timestamp_test.go
new file mode 100644
index 000000000..0fffb85a9
--- /dev/null
+++ b/pkg/timetype/timestamp_test.go
@@ -0,0 +1,95 @@
+package timetype
+
+// code adapted from https://github.com/moby/moby/blob/master/api/types/time/timestamp.go
+
+import (
+ "fmt"
+ "testing"
+ "time"
+)
+
+func TestGetTimestamp(t *testing.T) {
+ now := time.Now().In(time.UTC)
+ cases := []struct {
+ in, expected string
+ expectedErr bool
+ }{
+ // Partial RFC3339 strings get parsed with second precision
+ {"2006-01-02T15:04:05.999999999+07:00", "1136189045.999999999", false},
+ {"2006-01-02T15:04:05.999999999Z", "1136214245.999999999", false},
+ {"2006-01-02T15:04:05.999999999", "1136214245.999999999", false},
+ {"2006-01-02T15:04:05Z", "1136214245.000000000", false},
+ {"2006-01-02T15:04:05", "1136214245.000000000", false},
+ {"2006-01-02T15:04:0Z", "", true},
+ {"2006-01-02T15:04:0", "", true},
+ {"2006-01-02T15:04Z", "1136214240.000000000", false},
+ {"2006-01-02T15:04+00:00", "1136214240.000000000", false},
+ {"2006-01-02T15:04-00:00", "1136214240.000000000", false},
+ {"2006-01-02T15:04", "1136214240.000000000", false},
+ {"2006-01-02T15:0Z", "", true},
+ {"2006-01-02T15:0", "", true},
+ {"2006-01-02T15Z", "1136214000.000000000", false},
+ {"2006-01-02T15+00:00", "1136214000.000000000", false},
+ {"2006-01-02T15-00:00", "1136214000.000000000", false},
+ {"2006-01-02T15", "1136214000.000000000", false},
+ {"2006-01-02T1Z", "1136163600.000000000", false},
+ {"2006-01-02T1", "1136163600.000000000", false},
+ {"2006-01-02TZ", "", true},
+ {"2006-01-02T", "", true},
+ {"2006-01-02+00:00", "1136160000.000000000", false},
+ {"2006-01-02-00:00", "1136160000.000000000", false},
+ {"2006-01-02-00:01", "1136160060.000000000", false},
+ {"2006-01-02Z", "1136160000.000000000", false},
+ {"2006-01-02", "1136160000.000000000", false},
+ {"2015-05-13T20:39:09Z", "1431549549.000000000", false},
+
+ // unix timestamps returned as is
+ {"1136073600", "1136073600", false},
+ {"1136073600.000000001", "1136073600.000000001", false},
+ // Durations
+ {"1m", fmt.Sprintf("%d", now.Add(-1*time.Minute).Unix()), false},
+ {"1.5h", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix()), false},
+ {"1h30m", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix()), false},
+
+ {"invalid", "", true},
+ {"", "", true},
+ }
+
+ for _, c := range cases {
+ o, err := GetTimestamp(c.in, now)
+ if o != c.expected ||
+ (err == nil && c.expectedErr) ||
+ (err != nil && !c.expectedErr) {
+ t.Errorf("wrong value for '%s'. expected:'%s' got:'%s' with error: `%s`", c.in, c.expected, o, err)
+ t.Fail()
+ }
+ }
+}
+
+func TestParseTimestamps(t *testing.T) {
+ cases := []struct {
+ in string
+ def, expectedS, expectedN int64
+ expectedErr bool
+ }{
+ // unix timestamps
+ {"1136073600", 0, 1136073600, 0, false},
+ {"1136073600.000000001", 0, 1136073600, 1, false},
+ {"1136073600.0000000010", 0, 1136073600, 1, false},
+ {"1136073600.00000001", 0, 1136073600, 10, false},
+ {"foo.bar", 0, 0, 0, true},
+ {"1136073600.bar", 0, 1136073600, 0, true},
+ {"", -1, -1, 0, false},
+ }
+
+ for _, c := range cases {
+ s, n, err := ParseTimestamps(c.in, c.def)
+ if s != c.expectedS ||
+ n != c.expectedN ||
+ (err == nil && c.expectedErr) ||
+ (err != nil && !c.expectedErr) {
+ t.Errorf("wrong values for input `%s` with default `%d` expected:'%d'seconds and `%d`nanosecond got:'%d'seconds and `%d`nanoseconds with error: `%s`", c.in, c.def, c.expectedS, c.expectedN, s, n, err)
+ t.Fail()
+ }
+ }
+}
diff --git a/pkg/trust/trust.go b/pkg/trust/trust.go
index b1febbe81..60de099fa 100644
--- a/pkg/trust/trust.go
+++ b/pkg/trust/trust.go
@@ -139,7 +139,7 @@ func LoadAndMergeConfig(dirPath string) (*RegistryConfiguration, error) {
return &mergedConfig, nil
}
-// HaveMatchRegistry checks if trust settings for the registry have been configed in yaml file
+// HaveMatchRegistry checks if trust settings for the registry have been configured in yaml file
func HaveMatchRegistry(key string, registryConfigs *RegistryConfiguration) *RegistryNamespace {
searchKey := key
if !strings.Contains(searchKey, "/") {
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 633d8a124..6906b26d5 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -84,7 +84,7 @@ func ParseChanges(option string) (key string, vals []string, err error) {
if len(tokens) < 2 {
return "", []string{}, fmt.Errorf("invalid key value %s", option)
}
- key = strings.Trim(tokens[0], " ") // Need to trim whitespace part of delimeter.
+ key = strings.Trim(tokens[0], " ") // Need to trim whitespace part of delimiter.
val = tokens[1]
if strings.Contains(tokens[1], "[") && strings.Contains(tokens[1], "]") {
//Trim '[',']' if exist.
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index 8d44e6373..c27088805 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -21,7 +21,7 @@ import (
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/cmd/podman/varlink"
+ 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"
@@ -29,7 +29,7 @@ import (
"github.com/containers/libpod/pkg/util"
"github.com/containers/libpod/utils"
"github.com/containers/storage/pkg/archive"
- "github.com/opencontainers/image-spec/specs-go/v1"
+ v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -740,8 +740,8 @@ func (i *LibpodAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman.
}
// ImagesPrune ....
-func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall, all bool) error {
- prunedImages, err := i.Runtime.ImageRuntime().PruneImages(context.TODO(), all)
+func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall, all bool, filter []string) error {
+ prunedImages, err := i.Runtime.ImageRuntime().PruneImages(context.TODO(), all, []string{})
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}