podman create, start, getattachsocket

First pass at implement API endpoints for create and start.

Signed-off-by: baude <bbaude@redhat.com>

Closes: #805
Approved by: baude
This commit is contained in:
baude
2018-05-16 12:38:17 -05:00
committed by Atomic Bot
parent 687b165a73
commit 82feafecdd
20 changed files with 3647 additions and 2617 deletions

260
API.md
View File

@ -9,7 +9,7 @@ in the [API.md](https://github.com/projectatomic/libpod/blob/master/API.md) file
[func Commit(name: string, image_name: string, changes: []string, author: string, message: string, pause: bool) string](#Commit) [func Commit(name: string, image_name: string, changes: []string, author: string, message: string, pause: bool) string](#Commit)
[func CreateContainer() NotImplemented](#CreateContainer) [func CreateContainer(create: Create) string](#CreateContainer)
[func CreateImage() NotImplemented](#CreateImage) [func CreateImage() NotImplemented](#CreateImage)
@ -21,6 +21,8 @@ in the [API.md](https://github.com/projectatomic/libpod/blob/master/API.md) file
[func ExportImage(name: string, destination: string, compress: bool) string](#ExportImage) [func ExportImage(name: string, destination: string, compress: bool) string](#ExportImage)
[func GetAttachSockets(name: string) Sockets](#GetAttachSockets)
[func GetContainer(name: string) ListContainerData](#GetContainer) [func GetContainer(name: string) ListContainerData](#GetContainer)
[func GetContainerLogs(name: string) []string](#GetContainerLogs) [func GetContainerLogs(name: string) []string](#GetContainerLogs)
@ -69,7 +71,7 @@ in the [API.md](https://github.com/projectatomic/libpod/blob/master/API.md) file
[func SearchImage(name: string, limit: int) ImageSearch](#SearchImage) [func SearchImage(name: string, limit: int) ImageSearch](#SearchImage)
[func StartContainer() NotImplemented](#StartContainer) [func StartContainer(name: string) string](#StartContainer)
[func StopContainer(name: string, timeout: int) string](#StopContainer) [func StopContainer(name: string, timeout: int) string](#StopContainer)
@ -91,6 +93,14 @@ in the [API.md](https://github.com/projectatomic/libpod/blob/master/API.md) file
[type ContainerStats](#ContainerStats) [type ContainerStats](#ContainerStats)
[type Create](#Create)
[type CreateResourceConfig](#CreateResourceConfig)
[type IDMap](#IDMap)
[type IDMappingOptions](#IDMappingOptions)
[type ImageHistory](#ImageHistory) [type ImageHistory](#ImageHistory)
[type ImageInList](#ImageInList) [type ImageInList](#ImageInList)
@ -111,6 +121,8 @@ in the [API.md](https://github.com/projectatomic/libpod/blob/master/API.md) file
[type PodmanInfo](#PodmanInfo) [type PodmanInfo](#PodmanInfo)
[type Sockets](#Sockets)
[type StringResponse](#StringResponse) [type StringResponse](#StringResponse)
[type Version](#Version) [type Version](#Version)
@ -148,8 +160,8 @@ the resulting image's ID will be returned as a string.
### <a name="CreateContainer"></a>func CreateContainer ### <a name="CreateContainer"></a>func CreateContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method CreateContainer() [NotImplemented](#NotImplemented)</div> method CreateContainer(create: [Create](#Create)) [string](https://godoc.org/builtin#string)</div>
This method is not implemented yet. CreateContainer creates a new container from an image. It uses a (Create)[#Create] type for input.
### <a name="CreateImage"></a>func CreateImage ### <a name="CreateImage"></a>func CreateImage
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@ -182,6 +194,25 @@ method ExportImage(name: [string](https://godoc.org/builtin#string), destination
ExportImage takes the name or ID of an image and exports it to a destination like a tarball. There is also ExportImage takes the name or ID of an image and exports it to a destination like a tarball. There is also
a booleon option to force compression. Upon completion, the ID of the image is returned. If the image cannot a booleon option to force compression. Upon completion, the ID of the image is returned. If the image cannot
be found in local storage, an [ImageNotFound](#ImageNotFound) error will be returned. See also [ImportImage](ImportImage). be found in local storage, an [ImageNotFound](#ImageNotFound) error will be returned. See also [ImportImage](ImportImage).
### <a name="GetAttachSockets"></a>func GetAttachSockets
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method GetAttachSockets(name: [string](https://godoc.org/builtin#string)) [Sockets](#Sockets)</div>
GetAttachSockets takes the name or ID of an existing container. It returns file paths for two sockets needed
to properly communicate with a container. The first is the actual I/O socket that the container uses. The
second is a "control" socket where things like resizing the TTY events are sent. If the container cannot be
found, a [ContainerNotFound](#ContainerNotFound) error will be returned.
#### Example
~~~
$ varlink call -m unix:/run/io.projectatomic.podman/io.projectatomic.podman.GetAttachSockets '{"name": "b7624e775431219161"}'
{
"sockets": {
"container_id": "b7624e7754312191613245ce1a46844abee60025818fe3c3f3203435623a1eca",
"control_socket": "/var/lib/containers/storage/overlay-containers/b7624e7754312191613245ce1a46844abee60025818fe3c3f3203435623a1eca/userdata/ctl",
"io_socket": "/var/run/libpod/socket/b7624e7754312191613245ce1a46844abee60025818fe3c3f3203435623a1eca/attach"
}
}
~~~
### <a name="GetContainer"></a>func GetContainer ### <a name="GetContainer"></a>func GetContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@ -357,8 +388,8 @@ the image cannot be found in local storage; otherwise the ID of the image will b
method RemoveContainer(name: [string](https://godoc.org/builtin#string), force: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div> method RemoveContainer(name: [string](https://godoc.org/builtin#string), force: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
RemoveContainer takes requires the name or ID of container as well a boolean representing whether a running RemoveContainer takes requires the name or ID of container as well a boolean representing whether a running
container can be stopped and removed. Upon sucessful removal of the container, its ID is returned. If the container can be stopped and removed. Upon sucessful removal of the container, its ID is returned. If the
container cannot be found by name or ID, an [ContainerNotFound](#ContainerNotFound) error will be returned. container cannot be found by name or ID, a [ContainerNotFound](#ContainerNotFound) error will be returned.
#### Error #### Example
~~~ ~~~
$ varlink call -m unix:/run/podman/io.projectatomic.podman/io.projectatomic.podman.RemoveContainer '{"name": "62f4fd98cb57"}' $ varlink call -m unix:/run/podman/io.projectatomic.podman/io.projectatomic.podman.RemoveContainer '{"name": "62f4fd98cb57"}'
{ {
@ -407,8 +438,10 @@ ImageSearch structures which contain information about the image as well as its
### <a name="StartContainer"></a>func StartContainer ### <a name="StartContainer"></a>func StartContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method StartContainer() [NotImplemented](#NotImplemented)</div> method StartContainer(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div>
This method has not be implemented yet. StartContainer starts a created or stopped container. It takes the name or ID of container. It returns
the container ID once started. If the container cannot be found, a [ContainerNotFound](#ContainerNotFound)
error will be returned. See also [CreateContainer](#CreateContainer).
### <a name="StopContainer"></a>func StopContainer ### <a name="StopContainer"></a>func StopContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@ -446,7 +479,7 @@ This method has not be implemented yet.
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method WaitContainer(name: [string](https://godoc.org/builtin#string)) [int](https://godoc.org/builtin#int)</div> method WaitContainer(name: [string](https://godoc.org/builtin#string)) [int](https://godoc.org/builtin#int)</div>
WaitContainer takes the name of ID of a container and waits until the container stops. Upon stopping, the return WaitContainer takes the name or ID of a container and waits until the container stops. Upon stopping, the return
code of the container is returned. If the container container cannot be found by ID or name, code of the container is returned. If the container container cannot be found by ID or name,
a [ContainerNotFound](#ContainerNotFound) error is returned. a [ContainerNotFound](#ContainerNotFound) error is returned.
## Types ## Types
@ -529,6 +562,206 @@ block_output [int](https://godoc.org/builtin#int)
block_input [int](https://godoc.org/builtin#int) block_input [int](https://godoc.org/builtin#int)
pids [int](https://godoc.org/builtin#int) pids [int](https://godoc.org/builtin#int)
### <a name="Create"></a>type Create
Create is an input structure for creating containers. It closely resembles the
CreateConfig structure in libpod/pkg/spec.
args [[]string](#[]string)
cap_add [[]string](#[]string)
cap_drop [[]string](#[]string)
conmon_pidfile [string](https://godoc.org/builtin#string)
cgroup_parent [string](https://godoc.org/builtin#string)
command [[]string](#[]string)
detach [bool](https://godoc.org/builtin#bool)
devices [[]string](#[]string)
dns_opt [[]string](#[]string)
dns_search [[]string](#[]string)
dns_servers [[]string](#[]string)
entrypoint [[]string](#[]string)
env [map[string]](#map[string])
exposed_ports [[]string](#[]string)
gidmap [[]string](#[]string)
group_add [[]string](#[]string)
host_add [[]string](#[]string)
hostname [string](https://godoc.org/builtin#string)
image [string](https://godoc.org/builtin#string)
image_id [string](https://godoc.org/builtin#string)
builtin_imgvolumes [[]string](#[]string)
id_mappings [IDMappingOptions](#IDMappingOptions)
image_volume_type [string](https://godoc.org/builtin#string)
interactive [bool](https://godoc.org/builtin#bool)
ipc_mode [string](https://godoc.org/builtin#string)
labels [map[string]](#map[string])
log_driver [string](https://godoc.org/builtin#string)
log_driver_opt [[]string](#[]string)
name [string](https://godoc.org/builtin#string)
net_mode [string](https://godoc.org/builtin#string)
network [string](https://godoc.org/builtin#string)
pid_mode [string](https://godoc.org/builtin#string)
pod [string](https://godoc.org/builtin#string)
privileged [bool](https://godoc.org/builtin#bool)
publish [[]string](#[]string)
publish_all [bool](https://godoc.org/builtin#bool)
quiet [bool](https://godoc.org/builtin#bool)
readonly_rootfs [bool](https://godoc.org/builtin#bool)
resources [CreateResourceConfig](#CreateResourceConfig)
rm [bool](https://godoc.org/builtin#bool)
shm_dir [string](https://godoc.org/builtin#string)
stop_signal [int](https://godoc.org/builtin#int)
stop_timeout [int](https://godoc.org/builtin#int)
subuidmap [string](https://godoc.org/builtin#string)
subgidmap [string](https://godoc.org/builtin#string)
subuidname [string](https://godoc.org/builtin#string)
subgidname [string](https://godoc.org/builtin#string)
sys_ctl [map[string]](#map[string])
tmpfs [[]string](#[]string)
tty [bool](https://godoc.org/builtin#bool)
uidmap [[]string](#[]string)
userns_mode [string](https://godoc.org/builtin#string)
user [string](https://godoc.org/builtin#string)
uts_mode [string](https://godoc.org/builtin#string)
volumes [[]string](#[]string)
work_dir [string](https://godoc.org/builtin#string)
mount_label [string](https://godoc.org/builtin#string)
process_label [string](https://godoc.org/builtin#string)
no_new_privs [bool](https://godoc.org/builtin#bool)
apparmor_profile [string](https://godoc.org/builtin#string)
seccomp_profile_path [string](https://godoc.org/builtin#string)
security_opts [[]string](#[]string)
### <a name="CreateResourceConfig"></a>type CreateResourceConfig
CreateResourceConfig is an input structure used to describe host attributes during
container creation. It is only valid inside a (Create)[#Create] type.
blkio_weight [int](https://godoc.org/builtin#int)
blkio_weight_device [[]string](#[]string)
cpu_period [int](https://godoc.org/builtin#int)
cpu_quota [int](https://godoc.org/builtin#int)
cpu_rt_period [int](https://godoc.org/builtin#int)
cpu_rt_runtime [int](https://godoc.org/builtin#int)
cpu_shares [int](https://godoc.org/builtin#int)
cpus [float](https://golang.org/src/builtin/builtin.go#L58)
cpuset_cpus [string](https://godoc.org/builtin#string)
cpuset_mems [string](https://godoc.org/builtin#string)
device_read_bps [[]string](#[]string)
device_read_iops [[]string](#[]string)
device_write_bps [[]string](#[]string)
device_write_iops [[]string](#[]string)
disable_oomkiller [bool](https://godoc.org/builtin#bool)
kernel_memory [int](https://godoc.org/builtin#int)
memory [int](https://godoc.org/builtin#int)
memory_reservation [int](https://godoc.org/builtin#int)
memory_swap [int](https://godoc.org/builtin#int)
memory_swappiness [int](https://godoc.org/builtin#int)
oom_score_adj [int](https://godoc.org/builtin#int)
pids_limit [int](https://godoc.org/builtin#int)
shm_size [int](https://godoc.org/builtin#int)
ulimit [[]string](#[]string)
### <a name="IDMap"></a>type IDMap
IDMap is used to describe user name spaces during container creation
container_id [int](https://godoc.org/builtin#int)
host_id [int](https://godoc.org/builtin#int)
size [int](https://godoc.org/builtin#int)
### <a name="IDMappingOptions"></a>type IDMappingOptions
IDMappingOptions is an input structure used to described ids during container creation.
host_uid_mapping [bool](https://godoc.org/builtin#bool)
host_gid_mapping [bool](https://godoc.org/builtin#bool)
uid_map [IDMap](#IDMap)
gid_map [IDMap](#IDMap)
### <a name="ImageHistory"></a>type ImageHistory ### <a name="ImageHistory"></a>type ImageHistory
ImageHistory describes the returned structure from ImageHistory. ImageHistory describes the returned structure from ImageHistory.
@ -691,6 +924,15 @@ insecure_registries [[]string](#[]string)
store [InfoStore](#InfoStore) store [InfoStore](#InfoStore)
podman [InfoPodmanBinary](#InfoPodmanBinary) podman [InfoPodmanBinary](#InfoPodmanBinary)
### <a name="Sockets"></a>type Sockets
Sockets describes sockets location for a container
container_id [string](https://godoc.org/builtin#string)
io_socket [string](https://godoc.org/builtin#string)
control_socket [string](https://godoc.org/builtin#string)
### <a name="StringResponse"></a>type StringResponse ### <a name="StringResponse"></a>type StringResponse

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net"
"os" "os"
"strconv" "strconv"
"strings" "strings"
@ -21,23 +20,12 @@ import (
"github.com/projectatomic/libpod/libpod" "github.com/projectatomic/libpod/libpod"
"github.com/projectatomic/libpod/libpod/image" "github.com/projectatomic/libpod/libpod/image"
"github.com/projectatomic/libpod/pkg/inspect" "github.com/projectatomic/libpod/pkg/inspect"
cc "github.com/projectatomic/libpod/pkg/spec"
"github.com/projectatomic/libpod/pkg/util" "github.com/projectatomic/libpod/pkg/util"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
type mountType string
// Type constants
const (
// TypeBind is the type for mounting host dir
TypeBind mountType = "bind"
// TypeVolume is the type for remote storage volumes
// TypeVolume mountType = "volume" // re-enable upon use
// TypeTmpfs is the type for mounting tmpfs
TypeTmpfs mountType = "tmpfs"
)
var ( var (
defaultEnvVariables = map[string]string{ defaultEnvVariables = map[string]string{
"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
@ -45,100 +33,6 @@ var (
} }
) )
type createResourceConfig struct {
BlkioWeight uint16 // blkio-weight
BlkioWeightDevice []string // blkio-weight-device
CPUPeriod uint64 // cpu-period
CPUQuota int64 // cpu-quota
CPURtPeriod uint64 // cpu-rt-period
CPURtRuntime int64 // cpu-rt-runtime
CPUShares uint64 // cpu-shares
CPUs float64 // cpus
CPUsetCPUs string
CPUsetMems string // cpuset-mems
DeviceReadBps []string // device-read-bps
DeviceReadIOps []string // device-read-iops
DeviceWriteBps []string // device-write-bps
DeviceWriteIOps []string // device-write-iops
DisableOomKiller bool // oom-kill-disable
KernelMemory int64 // kernel-memory
Memory int64 //memory
MemoryReservation int64 // memory-reservation
MemorySwap int64 //memory-swap
MemorySwappiness int // memory-swappiness
OomScoreAdj int //oom-score-adj
PidsLimit int64 // pids-limit
ShmSize int64
Ulimit []string //ulimit
}
type createConfig struct {
Runtime *libpod.Runtime
Args []string
CapAdd []string // cap-add
CapDrop []string // cap-drop
CidFile string
ConmonPidFile string
CgroupParent string // cgroup-parent
Command []string
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
HostAdd []string //add-host
Hostname string //hostname
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 container.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 container.NetworkMode //net
Network string //network
NetworkAlias []string //network-alias
PidMode container.PidMode //pid
Pod string //pod
PortBindings nat.PortMap
Privileged bool //privileged
Publish []string //publish
PublishAll bool //publish-all
Quiet bool //quiet
ReadOnlyRootfs bool //read-only
Resources createResourceConfig
Rm bool //rm
ShmDir string
StopSignal syscall.Signal // stop-signal
StopTimeout uint // stop-timeout
Sysctl map[string]string //sysctl
Tmpfs []string // tmpfs
Tty bool //tty
UsernsMode container.UsernsMode //userns
User string //user
UtsMode container.UTSMode //uts
Volumes []string //volume
WorkDir string //workdir
MountLabel string //SecurityOpts
ProcessLabel string //SecurityOpts
NoNewPrivs bool //SecurityOpts
ApparmorProfile string //SecurityOpts
SeccompProfilePath string //SecurityOpts
SecurityOpts []string
}
var createDescription = "Creates a new container from the given image or" + var createDescription = "Creates a new container from the given image or" +
" storage and prepares it for running the specified command. The" + " storage and prepares it for running the specified command. The" +
" container ID is then printed to stdout. You can then start it at" + " container ID is then printed to stdout. You can then start it at" +
@ -205,7 +99,7 @@ func createCmd(c *cli.Context) error {
} }
useImageVolumes := createConfig.ImageVolumeType == "bind" useImageVolumes := createConfig.ImageVolumeType == "bind"
runtimeSpec, err := createConfigToOCISpec(createConfig) runtimeSpec, err := cc.CreateConfigToOCISpec(createConfig)
if err != nil { if err != nil {
return err return err
} }
@ -248,7 +142,7 @@ func createCmd(c *cli.Context) error {
return nil return nil
} }
func parseSecurityOpt(config *createConfig, securityOpts []string) error { func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error {
var ( var (
labelOpts []string labelOpts []string
err error err error
@ -338,95 +232,9 @@ func isPortInImagePorts(exposedPorts map[string]struct{}, port string) bool {
return false return false
} }
func exposedPorts(c *cli.Context, imageExposedPorts map[string]struct{}) (map[nat.Port][]nat.PortBinding, error) {
containerPorts := make(map[string]string)
// add expose ports from the image itself
for expose := range imageExposedPorts {
_, port := nat.SplitProtoPort(expose)
containerPorts[port] = ""
}
// add the expose ports from the user (--expose)
// can be single or a range
for _, expose := range c.StringSlice("expose") {
//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
_, port := nat.SplitProtoPort(expose)
//parse the start and end port and create a sequence of ports to expose
//if expose a port, the start and end port are the same
start, end, err := nat.ParsePortRange(port)
if err != nil {
return nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", expose, err)
}
for i := start; i <= end; i++ {
containerPorts[strconv.Itoa(int(i))] = ""
}
}
// parse user input'd port bindings
pbPorts, portBindings, err := nat.ParsePortSpecs(c.StringSlice("publish"))
if err != nil {
return nil, err
}
// delete exposed container ports if being used by -p
for i := range pbPorts {
delete(containerPorts, i.Port())
}
// iterate container ports and make port bindings from them
if c.Bool("publish-all") {
for e := range containerPorts {
//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
//proto, port := nat.SplitProtoPort(e)
p, err := nat.NewPort("tcp", e)
if err != nil {
return nil, err
}
rp, err := getRandomPort()
if err != nil {
return nil, err
}
logrus.Debug(fmt.Sprintf("Using random host port %d with container port %d", rp, p.Int()))
portBindings[p] = CreatePortBinding(rp, "")
}
}
// We need to see if any host ports are not populated and if so, we need to assign a
// random port to them.
for k, pb := range portBindings {
if pb[0].HostPort == "" {
hostPort, err := getRandomPort()
if err != nil {
return nil, err
}
logrus.Debug(fmt.Sprintf("Using random host port %d with container port %s", hostPort, k.Port()))
pb[0].HostPort = strconv.Itoa(hostPort)
}
}
return portBindings, nil
}
func getRandomPort() (int, error) {
l, err := net.Listen("tcp", ":0")
if err != nil {
return 0, errors.Wrapf(err, "unable to get free port")
}
defer l.Close()
_, randomPort, err := net.SplitHostPort(l.Addr().String())
if err != nil {
return 0, errors.Wrapf(err, "unable to determine free port")
}
rp, err := strconv.Atoi(randomPort)
if err != nil {
return 0, errors.Wrapf(err, "unable to convert random port to int")
}
return rp, nil
}
// Parses CLI options related to container creation into a config which can be // Parses CLI options related to container creation into a config which can be
// parsed into an OCI runtime spec // parsed into an OCI runtime spec
func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtime, imageName string, data *inspect.ImageData) (*createConfig, error) { func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtime, imageName string, data *inspect.ImageData) (*cc.CreateConfig, error) {
var ( var (
inputCommand, command []string inputCommand, command []string
memoryLimit, memoryReservation, memorySwap, memoryKernel int64 memoryLimit, memoryReservation, memorySwap, memoryKernel int64
@ -605,7 +413,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
} }
// EXPOSED PORTS // EXPOSED PORTS
portBindings, err := exposedPorts(c, data.ContainerConfig.ExposedPorts) portBindings, err := cc.ExposedPorts(c.StringSlice("expose"), c.StringSlice("publish"), c.Bool("publish-all"), data.ContainerConfig.ExposedPorts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -656,7 +464,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
return nil, errors.Errorf("invalid image-volume type %q. Pick one of bind, tmpfs, or ignore", c.String("image-volume")) return nil, errors.Errorf("invalid image-volume type %q. Pick one of bind, tmpfs, or ignore", c.String("image-volume"))
} }
config := &createConfig{ config := &cc.CreateConfig{
Runtime: runtime, Runtime: runtime,
BuiltinImgVolumes: ImageVolumes, BuiltinImgVolumes: ImageVolumes,
ConmonPidFile: c.String("conmon-pidfile"), ConmonPidFile: c.String("conmon-pidfile"),
@ -701,7 +509,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
PortBindings: portBindings, PortBindings: portBindings,
Quiet: c.Bool("quiet"), Quiet: c.Bool("quiet"),
ReadOnlyRootfs: c.Bool("read-only"), ReadOnlyRootfs: c.Bool("read-only"),
Resources: createResourceConfig{ Resources: cc.CreateResourceConfig{
BlkioWeight: blkioWeight, BlkioWeight: blkioWeight,
BlkioWeightDevice: c.StringSlice("blkio-weight-device"), BlkioWeightDevice: c.StringSlice("blkio-weight-device"),
CPUShares: c.Uint64("cpu-shares"), CPUShares: c.Uint64("cpu-shares"),
@ -756,12 +564,3 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
} }
return config, nil return config, nil
} }
//CreatePortBinding takes port (int) and IP (string) and creates an array of portbinding structs
func CreatePortBinding(hostPort int, hostIP string) []nat.PortBinding {
pb := nat.PortBinding{
HostPort: strconv.Itoa(hostPort),
}
pb.HostIP = hostIP
return []nat.PortBinding{pb}
}

View File

@ -8,6 +8,7 @@ import (
"github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/sysinfo"
"github.com/pkg/errors" "github.com/pkg/errors"
cc "github.com/projectatomic/libpod/pkg/spec"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -141,7 +142,7 @@ func validateVolumeOpts(option string) error {
return nil return nil
} }
func verifyContainerResources(config *createConfig, update bool) ([]string, error) { func verifyContainerResources(config *cc.CreateConfig, update bool) ([]string, error) {
warnings := []string{} warnings := []string{}
sysInfo := sysinfo.New(true) sysInfo := sysinfo.New(true)

View File

@ -11,6 +11,7 @@ import (
"github.com/projectatomic/libpod/cmd/podman/libpodruntime" "github.com/projectatomic/libpod/cmd/podman/libpodruntime"
"github.com/projectatomic/libpod/libpod" "github.com/projectatomic/libpod/libpod"
"github.com/projectatomic/libpod/pkg/inspect" "github.com/projectatomic/libpod/pkg/inspect"
cc "github.com/projectatomic/libpod/pkg/spec"
"github.com/projectatomic/libpod/pkg/util" "github.com/projectatomic/libpod/pkg/util"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
@ -183,7 +184,7 @@ func getCtrInspectInfo(ctr *libpod.Container, ctrInspectData *inspect.ContainerI
pidsLimit := getPidsInfo(spec) pidsLimit := getPidsInfo(spec)
cgroup := getCgroup(spec) cgroup := getCgroup(spec)
var createArtifact createConfig var createArtifact cc.CreateConfig
artifact, err := ctr.GetArtifact("create-config") artifact, err := ctr.GetArtifact("create-config")
if err == nil { if err == nil {
if err := json.Unmarshal(artifact, &createArtifact); err != nil { if err := json.Unmarshal(artifact, &createArtifact); err != nil {

View File

@ -16,7 +16,6 @@ import (
"strconv" "strconv"
"strings" "strings"
units "github.com/docker/go-units"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -107,40 +106,6 @@ func validateBlkioWeight(val int64) (int64, error) { //nolint
return -1, errors.Errorf("invalid blkio weight %q, should be between 10 and 1000", val) return -1, errors.Errorf("invalid blkio weight %q, should be between 10 and 1000", val)
} }
// weightDevice is a structure that holds device:weight pair
type weightDevice struct {
path string
weight uint16
}
func (w *weightDevice) String() string {
return fmt.Sprintf("%s:%d", w.path, w.weight)
}
// validateweightDevice validates that the specified string has a valid device-weight format
// for blkio-weight-device flag
func validateweightDevice(val string) (*weightDevice, error) {
split := strings.SplitN(val, ":", 2)
if len(split) != 2 {
return nil, fmt.Errorf("bad format: %s", val)
}
if !strings.HasPrefix(split[0], "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", val)
}
weight, err := strconv.ParseUint(split[1], 10, 0)
if err != nil {
return nil, fmt.Errorf("invalid weight for device: %s", val)
}
if weight > 0 && (weight < 10 || weight > 1000) {
return nil, fmt.Errorf("invalid weight for device: %s", val)
}
return &weightDevice{
path: split[0],
weight: uint16(weight),
}, nil
}
// parseDevice parses a device mapping string to a container.DeviceMapping struct // parseDevice parses a device mapping string to a container.DeviceMapping struct
// for device flag // for device flag
func parseDevice(device string) (*HostDevice, error) { //nolint func parseDevice(device string) (*HostDevice, error) { //nolint
@ -250,64 +215,6 @@ func validatePath(val string, validator func(string) bool) (string, error) {
return val, nil return val, nil
} }
// throttleDevice is a structure that holds device:rate_per_second pair
type throttleDevice struct {
path string
rate uint64
}
func (t *throttleDevice) String() string {
return fmt.Sprintf("%s:%d", t.path, t.rate)
}
// validateBpsDevice validates that the specified string has a valid device-rate format
// for device-read-bps and device-write-bps flags
func validateBpsDevice(val string) (*throttleDevice, error) {
split := strings.SplitN(val, ":", 2)
if len(split) != 2 {
return nil, fmt.Errorf("bad format: %s", val)
}
if !strings.HasPrefix(split[0], "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", val)
}
rate, err := units.RAMInBytes(split[1])
if err != nil {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
}
if rate < 0 {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
}
return &throttleDevice{
path: split[0],
rate: uint64(rate),
}, nil
}
// validateIOpsDevice validates that the specified string has a valid device-rate format
// for device-write-iops and device-read-iops flags
func validateIOpsDevice(val string) (*throttleDevice, error) { //nolint
split := strings.SplitN(val, ":", 2)
if len(split) != 2 {
return nil, fmt.Errorf("bad format: %s", val)
}
if !strings.HasPrefix(split[0], "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", val)
}
rate, err := strconv.ParseUint(split[1], 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val)
}
if rate < 0 {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val)
}
return &throttleDevice{
path: split[0],
rate: uint64(rate),
}, nil
}
// validateDNSSearch validates domain for resolvconf search configuration. // validateDNSSearch validates domain for resolvconf search configuration.
// A zero length domain is represented by a dot (.). // A zero length domain is represented by a dot (.).
// for dns-search flag // for dns-search flag
@ -805,18 +712,6 @@ func stringSlicetoUint32Slice(inputSlice []string) ([]uint32, error) {
return outputSlice, nil return outputSlice, nil
} }
func getLoggingPath(opts []string) string {
for _, opt := range opts {
arr := strings.SplitN(opt, "=", 2)
if len(arr) == 2 {
if strings.TrimSpace(arr[0]) == "path" {
return strings.TrimSpace(arr[1])
}
}
}
return ""
}
// validateFileName returns an error if filename contains ":" // validateFileName returns an error if filename contains ":"
// as it is currently not supported // as it is currently not supported
func validateFileName(filename string) error { func validateFileName(filename string) error {

View File

@ -16,6 +16,7 @@ import (
"github.com/projectatomic/libpod/cmd/podman/formats" "github.com/projectatomic/libpod/cmd/podman/formats"
"github.com/projectatomic/libpod/cmd/podman/libpodruntime" "github.com/projectatomic/libpod/cmd/podman/libpodruntime"
"github.com/projectatomic/libpod/libpod" "github.com/projectatomic/libpod/libpod"
cc "github.com/projectatomic/libpod/pkg/spec"
"github.com/projectatomic/libpod/pkg/util" "github.com/projectatomic/libpod/pkg/util"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
@ -401,7 +402,7 @@ func getTemplateOutput(containers []*libpod.Container, opts batchcontainer.PsOpt
createdAt := batchInfo.ConConfig.CreatedTime.Format("2006-01-02 15:04:05 -0700 MST") createdAt := batchInfo.ConConfig.CreatedTime.Format("2006-01-02 15:04:05 -0700 MST")
imageName := batchInfo.ConConfig.RootfsImageName imageName := batchInfo.ConConfig.RootfsImageName
var createArtifact createConfig var createArtifact cc.CreateConfig
artifact, err := ctr.GetArtifact("create-config") artifact, err := ctr.GetArtifact("create-config")
if err == nil { if err == nil {
if err := json.Unmarshal(artifact, &createArtifact); err != nil { if err := json.Unmarshal(artifact, &createArtifact); err != nil {

View File

@ -14,6 +14,7 @@ import (
"github.com/projectatomic/libpod/cmd/podman/libpodruntime" "github.com/projectatomic/libpod/cmd/podman/libpodruntime"
"github.com/projectatomic/libpod/libpod" "github.com/projectatomic/libpod/libpod"
"github.com/projectatomic/libpod/libpod/image" "github.com/projectatomic/libpod/libpod/image"
cc "github.com/projectatomic/libpod/pkg/spec"
"github.com/projectatomic/libpod/pkg/util" "github.com/projectatomic/libpod/pkg/util"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
@ -91,7 +92,7 @@ func runCmd(c *cli.Context) error {
} }
useImageVolumes := createConfig.ImageVolumeType == "bind" useImageVolumes := createConfig.ImageVolumeType == "bind"
runtimeSpec, err := createConfigToOCISpec(createConfig) runtimeSpec, err := cc.CreateConfigToOCISpec(createConfig)
if err != nil { if err != nil {
return err return err
} }

View File

@ -7,6 +7,7 @@ import (
ociv1 "github.com/opencontainers/image-spec/specs-go/v1" ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
spec "github.com/opencontainers/runtime-spec/specs-go" spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/projectatomic/libpod/pkg/inspect" "github.com/projectatomic/libpod/pkg/inspect"
cc "github.com/projectatomic/libpod/pkg/spec"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -80,7 +81,7 @@ func getRuntimeSpec(c *cli.Context) (*spec.Spec, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
runtimeSpec, err := createConfigToOCISpec(createConfig) runtimeSpec, err := cc.CreateConfigToOCISpec(createConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,821 +0,0 @@
package main
import (
"io/ioutil"
"os"
"strconv"
"strings"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/docker/daemon/caps"
"github.com/docker/docker/pkg/mount"
"github.com/docker/docker/profiles/seccomp"
"github.com/docker/go-units"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/devices"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/projectatomic/libpod/libpod"
ann "github.com/projectatomic/libpod/pkg/annotations"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
const cpuPeriod = 100000
func u32Ptr(i int64) *uint32 { u := uint32(i); return &u }
func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm }
func blockAccessToKernelFilesystems(config *createConfig, g *generate.Generator) {
if !config.Privileged {
for _, mp := range []string{
"/proc/kcore",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware",
} {
g.AddLinuxMaskedPaths(mp)
}
for _, rp := range []string{
"/proc/asound",
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger",
} {
g.AddLinuxReadonlyPaths(rp)
}
}
}
func addPidNS(config *createConfig, g *generate.Generator) error {
pidMode := config.PidMode
if pidMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.PIDNamespace))
}
if pidMode.IsContainer() {
logrus.Debug("using container pidmode")
}
return nil
}
func addUserNS(config *createConfig, g *generate.Generator) error {
if (len(config.IDMappings.UIDMap) > 0 || len(config.IDMappings.GIDMap) > 0) && !config.UsernsMode.IsHost() {
g.AddOrReplaceLinuxNamespace(spec.UserNamespace, "")
}
return nil
}
func addNetNS(config *createConfig, g *generate.Generator) error {
netMode := config.NetMode
if netMode.IsHost() {
logrus.Debug("Using host netmode")
return g.RemoveLinuxNamespace(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 netMode.IsContainer() {
logrus.Debug("Using container netmode")
} else {
return errors.Errorf("unknown network mode")
}
return nil
}
func addUTSNS(config *createConfig, g *generate.Generator) error {
utsMode := config.UtsMode
if utsMode.IsHost() {
return g.RemoveLinuxNamespace(spec.UTSNamespace)
}
return nil
}
func addIpcNS(config *createConfig, g *generate.Generator) error {
ipcMode := config.IpcMode
if ipcMode.IsHost() {
return g.RemoveLinuxNamespace(spec.IPCNamespace)
}
if ipcMode.IsContainer() {
logrus.Debug("Using container ipcmode")
}
return nil
}
func addRlimits(config *createConfig, g *generate.Generator) error {
var (
ul *units.Ulimit
err error
)
for _, u := range config.Resources.Ulimit {
if ul, err = units.ParseUlimit(u); err != nil {
return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u)
}
g.AddProcessRlimits("RLIMIT_"+strings.ToUpper(ul.Name), uint64(ul.Hard), uint64(ul.Soft))
}
return nil
}
func setupCapabilities(config *createConfig, configSpec *spec.Spec) error {
var err error
var caplist []string
caplist, err = caps.TweakCapabilities(configSpec.Process.Capabilities.Bounding, config.CapAdd, config.CapDrop)
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
return nil
}
func addDevice(g *generate.Generator, device string) error {
dev, err := devices.DeviceFromPath(device, "rwm")
if err != nil {
return errors.Wrapf(err, "%s is not a valid device", device)
}
linuxdev := spec.LinuxDevice{
Path: dev.Path,
Type: string(dev.Type),
Major: dev.Major,
Minor: dev.Minor,
FileMode: &dev.FileMode,
UID: &dev.Uid,
GID: &dev.Gid,
}
g.AddDevice(linuxdev)
g.AddLinuxResourcesDevice(true, string(dev.Type), &dev.Major, &dev.Minor, dev.Permissions)
return nil
}
// Parses information needed to create a container into an OCI runtime spec
func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
cgroupPerm := "ro"
g := generate.New()
g.HostSpecific = true
addCgroup := true
if config.Privileged {
cgroupPerm = "rw"
g.RemoveMount("/sys")
sysMnt := spec.Mount{
Destination: "/sys",
Type: "sysfs",
Source: "sysfs",
Options: []string{"nosuid", "noexec", "nodev", "rw"},
}
g.AddMount(sysMnt)
} else if !config.UsernsMode.IsHost() && config.NetMode.IsHost() {
addCgroup = false
g.RemoveMount("/sys")
sysMnt := spec.Mount{
Destination: "/sys",
Type: "bind",
Source: "/sys",
Options: []string{"nosuid", "noexec", "nodev", "ro", "rbind"},
}
g.AddMount(sysMnt)
}
if addCgroup {
cgroupMnt := spec.Mount{
Destination: "/sys/fs/cgroup",
Type: "cgroup",
Source: "cgroup",
Options: []string{"nosuid", "noexec", "nodev", "relatime", cgroupPerm},
}
g.AddMount(cgroupMnt)
}
g.SetProcessCwd(config.WorkDir)
g.SetProcessArgs(config.Command)
g.SetProcessTerminal(config.Tty)
for key, val := range config.GetAnnotations() {
g.AddAnnotation(key, val)
}
g.SetRootReadonly(config.ReadOnlyRootfs)
g.SetHostname(config.Hostname)
if config.Hostname != "" {
g.AddProcessEnv("HOSTNAME", config.Hostname)
}
for sysctlKey, sysctlVal := range config.Sysctl {
g.AddLinuxSysctl(sysctlKey, sysctlVal)
}
g.AddProcessEnv("container", "podman")
// RESOURCES - MEMORY
if config.Resources.Memory != 0 {
g.SetLinuxResourcesMemoryLimit(config.Resources.Memory)
}
if config.Resources.MemoryReservation != 0 {
g.SetLinuxResourcesMemoryReservation(config.Resources.MemoryReservation)
}
if config.Resources.MemorySwap != 0 {
g.SetLinuxResourcesMemorySwap(config.Resources.MemorySwap)
}
if config.Resources.KernelMemory != 0 {
g.SetLinuxResourcesMemoryKernel(config.Resources.KernelMemory)
}
if config.Resources.MemorySwappiness != -1 {
g.SetLinuxResourcesMemorySwappiness(uint64(config.Resources.MemorySwappiness))
}
g.SetLinuxResourcesMemoryDisableOOMKiller(config.Resources.DisableOomKiller)
g.SetProcessOOMScoreAdj(config.Resources.OomScoreAdj)
// RESOURCES - CPU
if config.Resources.CPUShares != 0 {
g.SetLinuxResourcesCPUShares(config.Resources.CPUShares)
}
if config.Resources.CPUQuota != 0 {
g.SetLinuxResourcesCPUQuota(config.Resources.CPUQuota)
}
if config.Resources.CPUPeriod != 0 {
g.SetLinuxResourcesCPUPeriod(config.Resources.CPUPeriod)
}
if config.Resources.CPUs != 0 {
g.SetLinuxResourcesCPUPeriod(cpuPeriod)
g.SetLinuxResourcesCPUQuota(int64(config.Resources.CPUs * cpuPeriod))
}
if config.Resources.CPURtRuntime != 0 {
g.SetLinuxResourcesCPURealtimeRuntime(config.Resources.CPURtRuntime)
}
if config.Resources.CPURtPeriod != 0 {
g.SetLinuxResourcesCPURealtimePeriod(config.Resources.CPURtPeriod)
}
if config.Resources.CPUsetCPUs != "" {
g.SetLinuxResourcesCPUCpus(config.Resources.CPUsetCPUs)
}
if config.Resources.CPUsetMems != "" {
g.SetLinuxResourcesCPUMems(config.Resources.CPUsetMems)
}
// Devices
if config.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.
if err := config.AddPrivilegedDevices(&g); err != nil {
return nil, err
}
} else {
for _, device := range config.Devices {
if err := addDevice(&g, device); err != nil {
return nil, err
}
}
}
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.SetProcessApparmorProfile(config.ApparmorProfile)
g.SetProcessSelinuxLabel(config.ProcessLabel)
g.SetLinuxMountLabel(config.MountLabel)
blockAccessToKernelFilesystems(config, &g)
// RESOURCES - PIDS
if config.Resources.PidsLimit != 0 {
g.SetLinuxResourcesPidsLimit(config.Resources.PidsLimit)
}
for _, i := range config.Tmpfs {
// Default options if nothing passed
options := []string{"rw", "noexec", "nosuid", "nodev", "size=65536k"}
spliti := strings.SplitN(i, ":", 2)
if len(spliti) > 1 {
if _, _, err := mount.ParseTmpfsOptions(spliti[1]); err != nil {
return nil, err
}
options = strings.Split(spliti[1], ",")
}
tmpfsMnt := spec.Mount{
Destination: spliti[0],
Type: "tmpfs",
Source: "tmpfs",
Options: append(options, "tmpcopyup"),
}
g.AddMount(tmpfsMnt)
}
for name, val := range config.Env {
g.AddProcessEnv(name, val)
}
if err := addRlimits(config, &g); err != nil {
return nil, err
}
if err := addPidNS(config, &g); err != nil {
return nil, err
}
if err := addUserNS(config, &g); err != nil {
return nil, err
}
if err := addNetNS(config, &g); err != nil {
return nil, err
}
if err := addUTSNS(config, &g); err != nil {
return nil, err
}
if err := addIpcNS(config, &g); err != nil {
return nil, err
}
configSpec := g.Spec()
// 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" {
if config.SeccompProfilePath != "" {
seccompProfile, err := ioutil.ReadFile(config.SeccompProfilePath)
if err != nil {
return nil, errors.Wrapf(err, "opening seccomp profile (%s) failed", config.SeccompProfilePath)
}
seccompConfig, err := seccomp.LoadProfile(string(seccompProfile), configSpec)
if err != nil {
return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath)
}
configSpec.Linux.Seccomp = seccompConfig
} else {
seccompConfig, err := seccomp.GetDefaultProfile(configSpec)
if err != nil {
return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath)
}
configSpec.Linux.Seccomp = seccompConfig
}
}
// Clear default Seccomp profile from Generator for privileged containers
if config.SeccompProfilePath == "unconfined" || config.Privileged {
configSpec.Linux.Seccomp = nil
}
// BIND MOUNTS
mounts, err := config.GetVolumeMounts(configSpec.Mounts)
if err != nil {
return nil, errors.Wrapf(err, "error getting volume mounts")
}
configSpec.Mounts = append(configSpec.Mounts, mounts...)
for _, mount := range configSpec.Mounts {
for _, opt := range mount.Options {
switch opt {
case "private", "rprivate", "slave", "rslave", "shared", "rshared":
if err := g.SetLinuxRootPropagation(opt); err != nil {
return nil, errors.Wrapf(err, "error setting root propagation for %q", mount.Destination)
}
}
}
}
// BLOCK IO
blkio, err := config.createBlockIO()
if err != nil {
return nil, errors.Wrapf(err, "error creating block io")
}
if blkio != nil {
configSpec.Linux.Resources.BlockIO = blkio
}
/*
//Annotations
Resources: &configSpec.LinuxResources{
BlockIO: &blkio,
//HugepageLimits:
Network: &configSpec.LinuxNetwork{
// ClassID *uint32
// Priorites []LinuxInterfacePriority
},
},
//CgroupsPath:
//Namespaces: []LinuxNamespace
// DefaultAction:
// Architectures
// Syscalls:
},
// RootfsPropagation
// MaskedPaths
// ReadonlyPaths:
// IntelRdt
},
}
*/
return configSpec, nil
}
const (
bps = iota
iops
)
func (c *createConfig) createBlockIO() (*spec.LinuxBlockIO, error) {
bio := &spec.LinuxBlockIO{}
bio.Weight = &c.Resources.BlkioWeight
if len(c.Resources.BlkioWeightDevice) > 0 {
var lwds []spec.LinuxWeightDevice
for _, i := range c.Resources.BlkioWeightDevice {
wd, err := validateweightDevice(i)
if err != nil {
return bio, errors.Wrapf(err, "invalid values for blkio-weight-device")
}
wdStat, err := getStatFromPath(wd.path)
if err != nil {
return bio, errors.Wrapf(err, "error getting stat from path %q", wd.path)
}
lwd := spec.LinuxWeightDevice{
Weight: &wd.weight,
}
lwd.Major = int64(unix.Major(wdStat.Rdev))
lwd.Minor = int64(unix.Minor(wdStat.Rdev))
lwds = append(lwds, lwd)
}
bio.WeightDevice = lwds
}
if len(c.Resources.DeviceReadBps) > 0 {
readBps, err := makeThrottleArray(c.Resources.DeviceReadBps, bps)
if err != nil {
return bio, err
}
bio.ThrottleReadBpsDevice = readBps
}
if len(c.Resources.DeviceWriteBps) > 0 {
writeBpds, err := makeThrottleArray(c.Resources.DeviceWriteBps, bps)
if err != nil {
return bio, err
}
bio.ThrottleWriteBpsDevice = writeBpds
}
if len(c.Resources.DeviceReadIOps) > 0 {
readIOps, err := makeThrottleArray(c.Resources.DeviceReadIOps, iops)
if err != nil {
return bio, err
}
bio.ThrottleReadIOPSDevice = readIOps
}
if len(c.Resources.DeviceWriteIOps) > 0 {
writeIOps, err := makeThrottleArray(c.Resources.DeviceWriteIOps, iops)
if err != nil {
return bio, err
}
bio.ThrottleWriteIOPSDevice = writeIOps
}
return bio, nil
}
func makeThrottleArray(throttleInput []string, rateType int) ([]spec.LinuxThrottleDevice, error) {
var (
ltds []spec.LinuxThrottleDevice
t *throttleDevice
err error
)
for _, i := range throttleInput {
if rateType == bps {
t, err = validateBpsDevice(i)
} else {
t, err = validateIOpsDevice(i)
}
if err != nil {
return []spec.LinuxThrottleDevice{}, err
}
ltdStat, err := getStatFromPath(t.path)
if err != nil {
return ltds, errors.Wrapf(err, "error getting stat from path %q", t.path)
}
ltd := spec.LinuxThrottleDevice{
Rate: t.rate,
}
ltd.Major = int64(unix.Major(ltdStat.Rdev))
ltd.Minor = int64(unix.Minor(ltdStat.Rdev))
ltds = append(ltds, ltd)
}
return ltds, nil
}
// GetAnnotations returns the all the annotations for the container
func (c *createConfig) GetAnnotations() map[string]string {
a := getDefaultAnnotations()
// TODO - Which annotations do we want added by default
// TODO - This should be added to the DB long term
if c.Tty {
a["io.kubernetes.cri-o.TTY"] = "true"
}
return a
}
func getDefaultAnnotations() map[string]string {
var annotations map[string]string
annotations = make(map[string]string)
annotations[ann.Annotations] = ""
annotations[ann.ContainerID] = ""
annotations[ann.ContainerName] = ""
annotations[ann.ContainerType] = "sandbox"
annotations[ann.Created] = ""
annotations[ann.HostName] = ""
annotations[ann.IP] = ""
annotations[ann.Image] = ""
annotations[ann.ImageName] = ""
annotations[ann.ImageRef] = ""
annotations[ann.KubeName] = ""
annotations[ann.Labels] = ""
annotations[ann.LogPath] = ""
annotations[ann.Metadata] = ""
annotations[ann.Name] = ""
annotations[ann.PrivilegedRuntime] = ""
annotations[ann.ResolvPath] = ""
annotations[ann.HostnamePath] = ""
annotations[ann.SandboxID] = ""
annotations[ann.SandboxName] = ""
annotations[ann.ShmPath] = ""
annotations[ann.MountPoint] = ""
annotations[ann.TrustedSandbox] = ""
annotations[ann.TTY] = "false"
annotations[ann.Stdin] = ""
annotations[ann.StdinOnce] = ""
annotations[ann.Volumes] = ""
return annotations
}
//GetVolumeMounts takes user provided input for bind mounts and creates Mount structs
func (c *createConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, error) {
var m []spec.Mount
var options []string
for _, i := range c.Volumes {
// We need to handle SELinux options better here, specifically :Z
spliti := strings.Split(i, ":")
if len(spliti) > 2 {
options = strings.Split(spliti[2], ",")
}
if libpod.MountExists(specMounts, spliti[1]) {
continue
}
options = append(options, "rbind")
var foundrw, foundro, foundz, foundZ bool
var rootProp string
for _, opt := range options {
switch opt {
case "rw":
foundrw = true
case "ro":
foundro = true
case "z":
foundz = true
case "Z":
foundZ = true
case "private", "rprivate", "slave", "rslave", "shared", "rshared":
rootProp = opt
}
}
if !foundrw && !foundro {
options = append(options, "rw")
}
if foundz {
if err := label.Relabel(spliti[0], c.MountLabel, true); err != nil {
return nil, errors.Wrapf(err, "relabel failed %q", spliti[0])
}
}
if foundZ {
if err := label.Relabel(spliti[0], c.MountLabel, false); err != nil {
return nil, errors.Wrapf(err, "relabel failed %q", spliti[0])
}
}
if rootProp == "" {
options = append(options, "private")
}
m = append(m, spec.Mount{
Destination: spliti[1],
Type: string(TypeBind),
Source: spliti[0],
Options: options,
})
}
// volumes from image config
if c.ImageVolumeType != "tmpfs" {
return m, nil
}
for vol := range c.BuiltinImgVolumes {
if libpod.MountExists(specMounts, vol) {
continue
}
mount := spec.Mount{
Destination: vol,
Type: string(TypeTmpfs),
Source: string(TypeTmpfs),
Options: []string{"rw", "noexec", "nosuid", "nodev", "tmpcopyup"},
}
m = append(m, mount)
}
return m, nil
}
//GetTmpfsMounts takes user provided input for Tmpfs mounts and creates Mount structs
func (c *createConfig) GetTmpfsMounts() []spec.Mount {
var m []spec.Mount
for _, i := range c.Tmpfs {
// Default options if nothing passed
options := []string{"rw", "noexec", "nosuid", "nodev", "size=65536k"}
spliti := strings.Split(i, ":")
destPath := spliti[0]
if len(spliti) > 1 {
options = strings.Split(spliti[1], ",")
}
m = append(m, spec.Mount{
Destination: destPath,
Type: string(TypeTmpfs),
Options: options,
Source: string(TypeTmpfs),
})
}
return m
}
func (c *createConfig) GetContainerCreateOptions() ([]libpod.CtrCreateOption, error) {
var options []libpod.CtrCreateOption
var portBindings []ocicni.PortMapping
var err error
// Uncomment after talking to mheon about unimplemented funcs
// options = append(options, libpod.WithLabels(c.labels))
if c.Interactive {
options = append(options, libpod.WithStdin())
}
if c.Name != "" {
logrus.Debugf("appending name %s", c.Name)
options = append(options, libpod.WithName(c.Name))
}
if len(c.PortBindings) > 0 {
portBindings, err = c.CreatePortBindings()
if err != nil {
return nil, errors.Wrapf(err, "unable to create port bindings")
}
}
if len(c.Volumes) != 0 {
// Volumes consist of multiple, comma-delineated fields
// The image spec only includes one part of that, so drop the
// others, if they are included
volumes := make([]string, 0, len(c.Volumes))
for _, vol := range c.Volumes {
volumes = append(volumes, strings.SplitN(vol, ":", 2)[0])
}
options = append(options, libpod.WithUserVolumes(volumes))
}
if len(c.Command) != 0 {
options = append(options, libpod.WithCommand(c.Command))
}
// Add entrypoint unconditionally
// If it's empty it's because it was explicitly set to "" or the image
// does not have one
options = append(options, libpod.WithEntrypoint(c.Entrypoint))
if c.NetMode.IsContainer() {
connectedCtr, err := c.Runtime.LookupContainer(c.NetMode.ConnectedContainer())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.NetMode.ConnectedContainer())
}
options = append(options, libpod.WithNetNSFrom(connectedCtr))
} else if !c.NetMode.IsHost() && !c.NetMode.IsNone() {
postConfigureNetNS := (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost()
options = append(options, libpod.WithNetNS([]ocicni.PortMapping{}, postConfigureNetNS))
options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS))
}
if c.PidMode.IsContainer() {
connectedCtr, err := c.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.IpcMode.IsContainer() {
connectedCtr, err := c.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.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 {
options = append(options, libpod.WithDNS(c.DNSServers))
}
if len(c.DNSOpt) > 0 {
options = append(options, libpod.WithDNSOption(c.DNSOpt))
}
if len(c.HostAdd) > 0 {
options = append(options, libpod.WithHosts(c.HostAdd))
}
logPath := getLoggingPath(c.LogDriverOpt)
if logPath != "" {
options = append(options, libpod.WithLogPath(logPath))
}
options = append(options, libpod.WithPrivileged(c.Privileged))
return options, nil
}
func getStatFromPath(path string) (unix.Stat_t, error) {
s := unix.Stat_t{}
err := unix.Stat(path, &s)
return s, err
}
// CreatePortBindings iterates ports mappings and exposed ports into a format CNI understands
func (c *createConfig) CreatePortBindings() ([]ocicni.PortMapping, error) {
var portBindings []ocicni.PortMapping
for containerPb, hostPb := range c.PortBindings {
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)
// CNI requires us to make both udp and tcp structs
pm.Protocol = "udp"
portBindings = append(portBindings, pm)
pm.Protocol = "tcp"
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 {
hostDevices, err := devices.HostDevices()
if err != nil {
return err
}
g.ClearLinuxDevices()
for _, d := range hostDevices {
g.AddDevice(Device(d))
}
g.AddLinuxResourcesDevice(true, "", nil, nil, "rwm")
return nil
}
// Device transforms a libcontainer configs.Device to a specs.LinuxDevice object.
func Device(d *configs.Device) spec.LinuxDevice {
return spec.LinuxDevice{
Type: string(d.Type),
Path: d.Path,
Major: d.Major,
Minor: d.Minor,
FileMode: fmPtr(int64(d.FileMode)),
UID: u32Ptr(int64(d.Uid)),
GID: u32Ptr(int64(d.Gid)),
}
}

View File

@ -173,6 +173,124 @@ type PodmanInfo (
podman: InfoPodmanBinary podman: InfoPodmanBinary
) )
# Sockets describes sockets location for a container
type Sockets(
container_id: string,
io_socket: string,
control_socket: string
)
# Create is an input structure for creating containers. It closely resembles the
# CreateConfig structure in libpod/pkg/spec.
type Create (
args: []string,
cap_add: []string,
cap_drop: []string,
conmon_pidfile: string,
cgroup_parent: string,
command: []string,
detach: bool,
devices: []string,
dns_opt: []string,
dns_search: []string,
dns_servers: []string,
entrypoint: []string,
env: [string]string,
exposed_ports: []string,
gidmap: []string,
group_add: []string,
host_add: []string,
hostname: string,
image: string,
image_id: string,
builtin_imgvolumes: []string,
id_mappings: IDMappingOptions,
image_volume_type: string,
interactive: bool,
ipc_mode: string,
labels: [string]string,
log_driver: string,
log_driver_opt: []string,
name: string,
net_mode: string,
network: string,
pid_mode: string,
pod: string,
privileged: bool,
publish: []string,
publish_all: bool,
quiet: bool,
readonly_rootfs: bool,
resources: CreateResourceConfig,
rm: bool,
shm_dir: string,
stop_signal: int,
stop_timeout: int,
subuidmap: string,
subgidmap: string,
subuidname: string,
subgidname: string,
sys_ctl: [string]string,
tmpfs: []string,
tty: bool,
uidmap: []string,
userns_mode: string,
user: string,
uts_mode: string,
volumes: []string,
work_dir: string,
mount_label: string,
process_label: string,
no_new_privs: bool,
apparmor_profile: string,
seccomp_profile_path: string,
security_opts: []string
)
# CreateResourceConfig is an input structure used to describe host attributes during
# container creation. It is only valid inside a (Create)[#Create] type.
type CreateResourceConfig (
blkio_weight: int,
blkio_weight_device: []string,
cpu_period: int,
cpu_quota: int,
cpu_rt_period: int,
cpu_rt_runtime: int,
cpu_shares: int,
cpus: float,
cpuset_cpus: string,
cpuset_mems: string,
device_read_bps: []string,
device_read_iops: []string,
device_write_bps: []string,
device_write_iops: []string,
disable_oomkiller: bool,
kernel_memory: int,
memory: int,
memory_reservation: int,
memory_swap: int,
memory_swappiness: int,
oom_score_adj: int,
pids_limit: int,
shm_size: int,
ulimit: []string
)
# IDMappingOptions is an input structure used to described ids during container creation.
type IDMappingOptions (
host_uid_mapping: bool,
host_gid_mapping: bool,
uid_map: IDMap,
gid_map: IDMap
)
# IDMap is used to describe user name spaces during container creation
type IDMap (
container_id: int,
host_id: int,
size: int
)
# Ping provides a response for developers to ensure their varlink setup is working. # Ping provides a response for developers to ensure their varlink setup is working.
# #### Example # #### Example
# ~~~ # ~~~
@ -202,8 +320,8 @@ method ListContainers() -> (containers: []ListContainerData)
# See also [ListContainers](ListContainers) and [InspectContainer](InspectContainer). # See also [ListContainers](ListContainers) and [InspectContainer](InspectContainer).
method GetContainer(name: string) -> (container: ListContainerData) method GetContainer(name: string) -> (container: ListContainerData)
# This method is not implemented yet. # CreateContainer creates a new container from an image. It uses a (Create)[#Create] type for input.
method CreateContainer() -> (notimplemented: NotImplemented) method CreateContainer(create: Create) -> (container: string)
# InspectContainer data takes a name or ID of a container returns the inspection # InspectContainer data takes a name or ID of a container returns the inspection
# data in string format. You can then serialize the string into JSON. A [ContainerNotFound](#ContainerNotFound) # data in string format. You can then serialize the string into JSON. A [ContainerNotFound](#ContainerNotFound)
@ -274,8 +392,10 @@ method GetContainerStats(name: string) -> (container: ContainerStats)
# This method has not be implemented yet. # This method has not be implemented yet.
method ResizeContainerTty() -> (notimplemented: NotImplemented) method ResizeContainerTty() -> (notimplemented: NotImplemented)
# This method has not be implemented yet. # StartContainer starts a created or stopped container. It takes the name or ID of container. It returns
method StartContainer() -> (notimplemented: NotImplemented) # the container ID once started. If the container cannot be found, a [ContainerNotFound](#ContainerNotFound)
# error will be returned. See also [CreateContainer](#CreateContainer).
method StartContainer(name: string) -> (container: string)
# StopContainer stops a container given a timeout. It takes the name or ID of a container as well as a # StopContainer stops a container given a timeout. It takes the name or ID of a container as well as a
# timeout value. The timeout value the time before a forceable stop to the container is applied. It # timeout value. The timeout value the time before a forceable stop to the container is applied. It
@ -320,15 +440,32 @@ method UnpauseContainer(name: string) -> (container: string)
# This method has not be implemented yet. # This method has not be implemented yet.
method AttachToContainer() -> (notimplemented: NotImplemented) method AttachToContainer() -> (notimplemented: NotImplemented)
# WaitContainer takes the name of ID of a container and waits until the container stops. Upon stopping, the return # GetAttachSockets takes the name or ID of an existing container. It returns file paths for two sockets needed
# to properly communicate with a container. The first is the actual I/O socket that the container uses. The
# second is a "control" socket where things like resizing the TTY events are sent. If the container cannot be
# found, a [ContainerNotFound](#ContainerNotFound) error will be returned.
# #### Example
# ~~~
# $ varlink call -m unix:/run/io.projectatomic.podman/io.projectatomic.podman.GetAttachSockets '{"name": "b7624e775431219161"}'
# {
# "sockets": {
# "container_id": "b7624e7754312191613245ce1a46844abee60025818fe3c3f3203435623a1eca",
# "control_socket": "/var/lib/containers/storage/overlay-containers/b7624e7754312191613245ce1a46844abee60025818fe3c3f3203435623a1eca/userdata/ctl",
# "io_socket": "/var/run/libpod/socket/b7624e7754312191613245ce1a46844abee60025818fe3c3f3203435623a1eca/attach"
# }
# }
# ~~~
method GetAttachSockets(name: string) -> (sockets: Sockets)
# WaitContainer takes the name or ID of a container and waits until the container stops. Upon stopping, the return
# code of the container is returned. If the container container cannot be found by ID or name, # code of the container is returned. If the container container cannot be found by ID or name,
# a [ContainerNotFound](#ContainerNotFound) error is returned. # a [ContainerNotFound](#ContainerNotFound) error is returned.
method WaitContainer(name: string) -> (exitcode: int) method WaitContainer(name: string) -> (exitcode: int)
# RemoveContainer takes requires the name or ID of container as well a boolean representing whether a running # RemoveContainer takes requires the name or ID of container as well a boolean representing whether a running
# container can be stopped and removed. Upon sucessful removal of the container, its ID is returned. If the # container can be stopped and removed. Upon sucessful removal of the container, its ID is returned. If the
# container cannot be found by name or ID, an [ContainerNotFound](#ContainerNotFound) error will be returned. # container cannot be found by name or ID, a [ContainerNotFound](#ContainerNotFound) error will be returned.
# #### Error # #### Example
# ~~~ # ~~~
# $ varlink call -m unix:/run/podman/io.projectatomic.podman/io.projectatomic.podman.RemoveContainer '{"name": "62f4fd98cb57"}' # $ varlink call -m unix:/run/podman/io.projectatomic.podman/io.projectatomic.podman.RemoveContainer '{"name": "62f4fd98cb57"}'
# { # {

File diff suppressed because it is too large Load Diff

View File

@ -81,9 +81,9 @@ func (c *Container) attachContainerSocket(resize <-chan remotecommand.TerminalSi
logrus.Warnf("Failed to write to control file to resize terminal: %v", err) logrus.Warnf("Failed to write to control file to resize terminal: %v", err)
} }
}) })
logrus.Debug("connecting to socket ", c.attachSocketPath()) logrus.Debug("connecting to socket ", c.AttachSocketPath())
conn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: c.attachSocketPath(), Net: "unixpacket"}) conn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: c.AttachSocketPath(), Net: "unixpacket"})
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to connect to container's attach socket: %v") return errors.Wrapf(err, "failed to connect to container's attach socket: %v")
} }

View File

@ -106,14 +106,20 @@ func (c *Container) rwSize() (int64, error) {
return c.runtime.store.DiffSize(layer.Parent, layer.ID) return c.runtime.store.DiffSize(layer.Parent, layer.ID)
} }
// The path to the container's root filesystem - where the OCI spec will be // bundlePath returns the path to the container's root filesystem - where the OCI spec will be
// placed, amongst other things // placed, amongst other things
func (c *Container) bundlePath() string { func (c *Container) bundlePath() string {
return c.config.StaticDir return c.config.StaticDir
} }
// Retrieves the path of the container's attach socket // ControlSocketPath returns the path to the containers control socket for things like tty
func (c *Container) attachSocketPath() string { // resizing
func (c *Container) ControlSocketPath() string {
return filepath.Join(c.bundlePath(), "ctl")
}
// AttachSocketPath retrieves the path of the container's attach socket
func (c *Container) AttachSocketPath() string {
return filepath.Join(c.runtime.ociRuntime.socketsDir, c.ID(), "attach") return filepath.Join(c.runtime.ociRuntime.socketsDir, c.ID(), "attach")
} }

508
pkg/spec/createconfig.go Normal file
View File

@ -0,0 +1,508 @@
package createconfig
import (
"os"
"strconv"
"strings"
"syscall"
"github.com/containers/storage"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/docker/api/types/container"
"github.com/docker/go-connections/nat"
"github.com/opencontainers/runc/libcontainer/devices"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/projectatomic/libpod/libpod"
ann "github.com/projectatomic/libpod/pkg/annotations"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
type mountType string
// Type constants
const (
bps = iota
iops
// TypeBind is the type for mounting host dir
TypeBind mountType = "bind"
// TypeVolume is the type for remote storage volumes
// TypeVolume mountType = "volume" // re-enable upon use
// TypeTmpfs is the type for mounting tmpfs
TypeTmpfs mountType = "tmpfs"
)
// CreateResourceConfig represents resource elements in CreateConfig
// structures
type CreateResourceConfig struct {
BlkioWeight uint16 // blkio-weight
BlkioWeightDevice []string // blkio-weight-device
CPUPeriod uint64 // cpu-period
CPUQuota int64 // cpu-quota
CPURtPeriod uint64 // cpu-rt-period
CPURtRuntime int64 // cpu-rt-runtime
CPUShares uint64 // cpu-shares
CPUs float64 // cpus
CPUsetCPUs string
CPUsetMems string // cpuset-mems
DeviceReadBps []string // device-read-bps
DeviceReadIOps []string // device-read-iops
DeviceWriteBps []string // device-write-bps
DeviceWriteIOps []string // device-write-iops
DisableOomKiller bool // oom-kill-disable
KernelMemory int64 // kernel-memory
Memory int64 //memory
MemoryReservation int64 // memory-reservation
MemorySwap int64 //memory-swap
MemorySwappiness int // memory-swappiness
OomScoreAdj int //oom-score-adj
PidsLimit int64 // pids-limit
ShmSize int64
Ulimit []string //ulimit
}
// CreateConfig is a pre OCI spec structure. It represents user input from varlink or the CLI
type CreateConfig struct {
Runtime *libpod.Runtime
Args []string
CapAdd []string // cap-add
CapDrop []string // cap-drop
CidFile string
ConmonPidFile string
CgroupParent string // cgroup-parent
Command []string
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
HostAdd []string //add-host
Hostname string //hostname
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 container.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 container.NetworkMode //net
Network string //network
NetworkAlias []string //network-alias
PidMode container.PidMode //pid
Pod string //pod
PortBindings nat.PortMap
Privileged bool //privileged
Publish []string //publish
PublishAll bool //publish-all
Quiet bool //quiet
ReadOnlyRootfs bool //read-only
Resources CreateResourceConfig
Rm bool //rm
ShmDir string
StopSignal syscall.Signal // stop-signal
StopTimeout uint // stop-timeout
Sysctl map[string]string //sysctl
Tmpfs []string // tmpfs
Tty bool //tty
UsernsMode container.UsernsMode //userns
User string //user
UtsMode container.UTSMode //uts
Volumes []string //volume
WorkDir string //workdir
MountLabel string //SecurityOpts
ProcessLabel string //SecurityOpts
NoNewPrivs bool //SecurityOpts
ApparmorProfile string //SecurityOpts
SeccompProfilePath string //SecurityOpts
SecurityOpts []string
}
func u32Ptr(i int64) *uint32 { u := uint32(i); return &u }
func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm }
// CreateBlockIO returns a LinuxBlockIO struct from a CreateConfig
func (c *CreateConfig) CreateBlockIO() (*spec.LinuxBlockIO, error) {
bio := &spec.LinuxBlockIO{}
bio.Weight = &c.Resources.BlkioWeight
if len(c.Resources.BlkioWeightDevice) > 0 {
var lwds []spec.LinuxWeightDevice
for _, i := range c.Resources.BlkioWeightDevice {
wd, err := validateweightDevice(i)
if err != nil {
return bio, errors.Wrapf(err, "invalid values for blkio-weight-device")
}
wdStat, err := getStatFromPath(wd.path)
if err != nil {
return bio, errors.Wrapf(err, "error getting stat from path %q", wd.path)
}
lwd := spec.LinuxWeightDevice{
Weight: &wd.weight,
}
lwd.Major = int64(unix.Major(wdStat.Rdev))
lwd.Minor = int64(unix.Minor(wdStat.Rdev))
lwds = append(lwds, lwd)
}
bio.WeightDevice = lwds
}
if len(c.Resources.DeviceReadBps) > 0 {
readBps, err := makeThrottleArray(c.Resources.DeviceReadBps, bps)
if err != nil {
return bio, err
}
bio.ThrottleReadBpsDevice = readBps
}
if len(c.Resources.DeviceWriteBps) > 0 {
writeBpds, err := makeThrottleArray(c.Resources.DeviceWriteBps, bps)
if err != nil {
return bio, err
}
bio.ThrottleWriteBpsDevice = writeBpds
}
if len(c.Resources.DeviceReadIOps) > 0 {
readIOps, err := makeThrottleArray(c.Resources.DeviceReadIOps, iops)
if err != nil {
return bio, err
}
bio.ThrottleReadIOPSDevice = readIOps
}
if len(c.Resources.DeviceWriteIOps) > 0 {
writeIOps, err := makeThrottleArray(c.Resources.DeviceWriteIOps, iops)
if err != nil {
return bio, err
}
bio.ThrottleWriteIOPSDevice = writeIOps
}
return bio, nil
}
func makeThrottleArray(throttleInput []string, rateType int) ([]spec.LinuxThrottleDevice, error) {
var (
ltds []spec.LinuxThrottleDevice
t *throttleDevice
err error
)
for _, i := range throttleInput {
if rateType == bps {
t, err = validateBpsDevice(i)
} else {
t, err = validateIOpsDevice(i)
}
if err != nil {
return []spec.LinuxThrottleDevice{}, err
}
ltdStat, err := getStatFromPath(t.path)
if err != nil {
return ltds, errors.Wrapf(err, "error getting stat from path %q", t.path)
}
ltd := spec.LinuxThrottleDevice{
Rate: t.rate,
}
ltd.Major = int64(unix.Major(ltdStat.Rdev))
ltd.Minor = int64(unix.Minor(ltdStat.Rdev))
ltds = append(ltds, ltd)
}
return ltds, nil
}
// GetAnnotations returns the all the annotations for the container
func (c *CreateConfig) GetAnnotations() map[string]string {
a := getDefaultAnnotations()
// TODO - Which annotations do we want added by default
// TODO - This should be added to the DB long term
if c.Tty {
a["io.kubernetes.cri-o.TTY"] = "true"
}
return a
}
func getDefaultAnnotations() map[string]string {
var annotations map[string]string
annotations = make(map[string]string)
annotations[ann.Annotations] = ""
annotations[ann.ContainerID] = ""
annotations[ann.ContainerName] = ""
annotations[ann.ContainerType] = "sandbox"
annotations[ann.Created] = ""
annotations[ann.HostName] = ""
annotations[ann.IP] = ""
annotations[ann.Image] = ""
annotations[ann.ImageName] = ""
annotations[ann.ImageRef] = ""
annotations[ann.KubeName] = ""
annotations[ann.Labels] = ""
annotations[ann.LogPath] = ""
annotations[ann.Metadata] = ""
annotations[ann.Name] = ""
annotations[ann.PrivilegedRuntime] = ""
annotations[ann.ResolvPath] = ""
annotations[ann.HostnamePath] = ""
annotations[ann.SandboxID] = ""
annotations[ann.SandboxName] = ""
annotations[ann.ShmPath] = ""
annotations[ann.MountPoint] = ""
annotations[ann.TrustedSandbox] = ""
annotations[ann.TTY] = "false"
annotations[ann.Stdin] = ""
annotations[ann.StdinOnce] = ""
annotations[ann.Volumes] = ""
return annotations
}
//GetVolumeMounts takes user provided input for bind mounts and creates Mount structs
func (c *CreateConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, error) {
var m []spec.Mount
var options []string
for _, i := range c.Volumes {
// We need to handle SELinux options better here, specifically :Z
spliti := strings.Split(i, ":")
if len(spliti) > 2 {
options = strings.Split(spliti[2], ",")
}
if libpod.MountExists(specMounts, spliti[1]) {
continue
}
options = append(options, "rbind")
var foundrw, foundro, foundz, foundZ bool
var rootProp string
for _, opt := range options {
switch opt {
case "rw":
foundrw = true
case "ro":
foundro = true
case "z":
foundz = true
case "Z":
foundZ = true
case "private", "rprivate", "slave", "rslave", "shared", "rshared":
rootProp = opt
}
}
if !foundrw && !foundro {
options = append(options, "rw")
}
if foundz {
if err := label.Relabel(spliti[0], c.MountLabel, true); err != nil {
return nil, errors.Wrapf(err, "relabel failed %q", spliti[0])
}
}
if foundZ {
if err := label.Relabel(spliti[0], c.MountLabel, false); err != nil {
return nil, errors.Wrapf(err, "relabel failed %q", spliti[0])
}
}
if rootProp == "" {
options = append(options, "private")
}
m = append(m, spec.Mount{
Destination: spliti[1],
Type: string(TypeBind),
Source: spliti[0],
Options: options,
})
}
// volumes from image config
if c.ImageVolumeType != "tmpfs" {
return m, nil
}
for vol := range c.BuiltinImgVolumes {
if libpod.MountExists(specMounts, vol) {
continue
}
mount := spec.Mount{
Destination: vol,
Type: string(TypeTmpfs),
Source: string(TypeTmpfs),
Options: []string{"rw", "noexec", "nosuid", "nodev", "tmpcopyup"},
}
m = append(m, mount)
}
return m, nil
}
//GetTmpfsMounts takes user provided input for Tmpfs mounts and creates Mount structs
func (c *CreateConfig) GetTmpfsMounts() []spec.Mount {
var m []spec.Mount
for _, i := range c.Tmpfs {
// Default options if nothing passed
options := []string{"rw", "noexec", "nosuid", "nodev", "size=65536k"}
spliti := strings.Split(i, ":")
destPath := spliti[0]
if len(spliti) > 1 {
options = strings.Split(spliti[1], ",")
}
m = append(m, spec.Mount{
Destination: destPath,
Type: string(TypeTmpfs),
Options: options,
Source: string(TypeTmpfs),
})
}
return m
}
// GetContainerCreateOptions takes a CreateConfig and returns a slice of CtrCreateOptions
func (c *CreateConfig) GetContainerCreateOptions() ([]libpod.CtrCreateOption, error) {
var options []libpod.CtrCreateOption
var portBindings []ocicni.PortMapping
var err error
// Uncomment after talking to mheon about unimplemented funcs
// options = append(options, libpod.WithLabels(c.labels))
if c.Interactive {
options = append(options, libpod.WithStdin())
}
if c.Name != "" {
logrus.Debugf("appending name %s", c.Name)
options = append(options, libpod.WithName(c.Name))
}
if len(c.PortBindings) > 0 {
portBindings, err = c.CreatePortBindings()
if err != nil {
return nil, errors.Wrapf(err, "unable to create port bindings")
}
}
if len(c.Volumes) != 0 {
// Volumes consist of multiple, comma-delineated fields
// The image spec only includes one part of that, so drop the
// others, if they are included
volumes := make([]string, 0, len(c.Volumes))
for _, vol := range c.Volumes {
volumes = append(volumes, strings.SplitN(vol, ":", 2)[0])
}
options = append(options, libpod.WithUserVolumes(volumes))
}
if len(c.Command) != 0 {
options = append(options, libpod.WithCommand(c.Command))
}
// Add entrypoint unconditionally
// If it's empty it's because it was explicitly set to "" or the image
// does not have one
options = append(options, libpod.WithEntrypoint(c.Entrypoint))
if c.NetMode.IsContainer() {
connectedCtr, err := c.Runtime.LookupContainer(c.NetMode.ConnectedContainer())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.NetMode.ConnectedContainer())
}
options = append(options, libpod.WithNetNSFrom(connectedCtr))
} else if !c.NetMode.IsHost() && !c.NetMode.IsNone() {
postConfigureNetNS := (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost()
options = append(options, libpod.WithNetNS([]ocicni.PortMapping{}, postConfigureNetNS))
options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS))
}
if c.PidMode.IsContainer() {
connectedCtr, err := c.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.IpcMode.IsContainer() {
connectedCtr, err := c.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.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 {
options = append(options, libpod.WithDNS(c.DNSServers))
}
if len(c.DNSOpt) > 0 {
options = append(options, libpod.WithDNSOption(c.DNSOpt))
}
if len(c.HostAdd) > 0 {
options = append(options, libpod.WithHosts(c.HostAdd))
}
logPath := getLoggingPath(c.LogDriverOpt)
if logPath != "" {
options = append(options, libpod.WithLogPath(logPath))
}
options = append(options, libpod.WithPrivileged(c.Privileged))
return options, nil
}
// CreatePortBindings iterates ports mappings and exposed ports into a format CNI understands
func (c *CreateConfig) CreatePortBindings() ([]ocicni.PortMapping, error) {
var portBindings []ocicni.PortMapping
for containerPb, hostPb := range c.PortBindings {
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)
// CNI requires us to make both udp and tcp structs
pm.Protocol = "udp"
portBindings = append(portBindings, pm)
pm.Protocol = "tcp"
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 {
hostDevices, err := devices.HostDevices()
if err != nil {
return err
}
g.ClearLinuxDevices()
for _, d := range hostDevices {
g.AddDevice(Device(d))
}
g.AddLinuxResourcesDevice(true, "", nil, nil, "rwm")
return nil
}
func getStatFromPath(path string) (unix.Stat_t, error) {
s := unix.Stat_t{}
err := unix.Stat(path, &s)
return s, err
}

128
pkg/spec/parse.go Normal file
View File

@ -0,0 +1,128 @@
package createconfig
import (
"fmt"
"strconv"
"strings"
"github.com/docker/go-units"
"github.com/opencontainers/runc/libcontainer/configs"
spec "github.com/opencontainers/runtime-spec/specs-go"
)
// weightDevice is a structure that holds device:weight pair
type weightDevice struct {
path string
weight uint16
}
func (w *weightDevice) String() string {
return fmt.Sprintf("%s:%d", w.path, w.weight)
}
// validateweightDevice validates that the specified string has a valid device-weight format
// for blkio-weight-device flag
func validateweightDevice(val string) (*weightDevice, error) {
split := strings.SplitN(val, ":", 2)
if len(split) != 2 {
return nil, fmt.Errorf("bad format: %s", val)
}
if !strings.HasPrefix(split[0], "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", val)
}
weight, err := strconv.ParseUint(split[1], 10, 0)
if err != nil {
return nil, fmt.Errorf("invalid weight for device: %s", val)
}
if weight > 0 && (weight < 10 || weight > 1000) {
return nil, fmt.Errorf("invalid weight for device: %s", val)
}
return &weightDevice{
path: split[0],
weight: uint16(weight),
}, nil
}
// throttleDevice is a structure that holds device:rate_per_second pair
type throttleDevice struct {
path string
rate uint64
}
func (t *throttleDevice) String() string {
return fmt.Sprintf("%s:%d", t.path, t.rate)
}
// validateBpsDevice validates that the specified string has a valid device-rate format
// for device-read-bps and device-write-bps flags
func validateBpsDevice(val string) (*throttleDevice, error) {
split := strings.SplitN(val, ":", 2)
if len(split) != 2 {
return nil, fmt.Errorf("bad format: %s", val)
}
if !strings.HasPrefix(split[0], "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", val)
}
rate, err := units.RAMInBytes(split[1])
if err != nil {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
}
if rate < 0 {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
}
return &throttleDevice{
path: split[0],
rate: uint64(rate),
}, nil
}
// validateIOpsDevice validates that the specified string has a valid device-rate format
// for device-write-iops and device-read-iops flags
func validateIOpsDevice(val string) (*throttleDevice, error) { //nolint
split := strings.SplitN(val, ":", 2)
if len(split) != 2 {
return nil, fmt.Errorf("bad format: %s", val)
}
if !strings.HasPrefix(split[0], "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", val)
}
rate, err := strconv.ParseUint(split[1], 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val)
}
if rate < 0 {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val)
}
return &throttleDevice{
path: split[0],
rate: uint64(rate),
}, nil
}
func getLoggingPath(opts []string) string {
for _, opt := range opts {
arr := strings.SplitN(opt, "=", 2)
if len(arr) == 2 {
if strings.TrimSpace(arr[0]) == "path" {
return strings.TrimSpace(arr[1])
}
}
}
return ""
}
// Device transforms a libcontainer configs.Device to a specs.LinuxDevice object.
func Device(d *configs.Device) spec.LinuxDevice {
return spec.LinuxDevice{
Type: string(d.Type),
Path: d.Path,
Major: d.Major,
Minor: d.Minor,
FileMode: fmPtr(int64(d.FileMode)),
UID: u32Ptr(int64(d.Uid)),
GID: u32Ptr(int64(d.Gid)),
}
}

107
pkg/spec/ports.go Normal file
View File

@ -0,0 +1,107 @@
package createconfig
import (
"fmt"
"net"
"strconv"
"github.com/docker/go-connections/nat"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// ExposedPorts parses user and image ports and returns binding information
func ExposedPorts(expose, publish []string, publishAll bool, imageExposedPorts map[string]struct{}) (map[nat.Port][]nat.PortBinding, error) {
containerPorts := make(map[string]string)
// add expose ports from the image itself
for expose := range imageExposedPorts {
_, port := nat.SplitProtoPort(expose)
containerPorts[port] = ""
}
// add the expose ports from the user (--expose)
// can be single or a range
for _, expose := range expose {
//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
_, port := nat.SplitProtoPort(expose)
//parse the start and end port and create a sequence of ports to expose
//if expose a port, the start and end port are the same
start, end, err := nat.ParsePortRange(port)
if err != nil {
return nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", expose, err)
}
for i := start; i <= end; i++ {
containerPorts[strconv.Itoa(int(i))] = ""
}
}
// parse user input'd port bindings
pbPorts, portBindings, err := nat.ParsePortSpecs(publish)
if err != nil {
return nil, err
}
// delete exposed container ports if being used by -p
for i := range pbPorts {
delete(containerPorts, i.Port())
}
// iterate container ports and make port bindings from them
if publishAll {
for e := range containerPorts {
//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
//proto, port := nat.SplitProtoPort(e)
p, err := nat.NewPort("tcp", e)
if err != nil {
return nil, err
}
rp, err := getRandomPort()
if err != nil {
return nil, err
}
logrus.Debug(fmt.Sprintf("Using random host port %d with container port %d", rp, p.Int()))
portBindings[p] = CreatePortBinding(rp, "")
}
}
// We need to see if any host ports are not populated and if so, we need to assign a
// random port to them.
for k, pb := range portBindings {
if pb[0].HostPort == "" {
hostPort, err := getRandomPort()
if err != nil {
return nil, err
}
logrus.Debug(fmt.Sprintf("Using random host port %d with container port %s", hostPort, k.Port()))
pb[0].HostPort = strconv.Itoa(hostPort)
}
}
return portBindings, nil
}
func getRandomPort() (int, error) {
l, err := net.Listen("tcp", ":0")
if err != nil {
return 0, errors.Wrapf(err, "unable to get free port")
}
defer l.Close()
_, randomPort, err := net.SplitHostPort(l.Addr().String())
if err != nil {
return 0, errors.Wrapf(err, "unable to determine free port")
}
rp, err := strconv.Atoi(randomPort)
if err != nil {
return 0, errors.Wrapf(err, "unable to convert random port to int")
}
return rp, nil
}
//CreatePortBinding takes port (int) and IP (string) and creates an array of portbinding structs
func CreatePortBinding(hostPort int, hostIP string) []nat.PortBinding {
pb := nat.PortBinding{
HostPort: strconv.Itoa(hostPort),
}
pb.HostIP = hostIP
return []nat.PortBinding{pb}
}

422
pkg/spec/spec.go Normal file
View File

@ -0,0 +1,422 @@
package createconfig
import (
"strings"
"github.com/docker/docker/daemon/caps"
"github.com/docker/docker/pkg/mount"
"github.com/docker/docker/profiles/seccomp"
"github.com/docker/go-units"
"github.com/opencontainers/runc/libcontainer/devices"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"io/ioutil"
)
const cpuPeriod = 100000
// CreateConfigToOCISpec parses information needed to create a container into an OCI runtime spec
func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
cgroupPerm := "ro"
g := generate.New()
g.HostSpecific = true
addCgroup := true
if config.Privileged {
cgroupPerm = "rw"
g.RemoveMount("/sys")
sysMnt := spec.Mount{
Destination: "/sys",
Type: "sysfs",
Source: "sysfs",
Options: []string{"nosuid", "noexec", "nodev", "rw"},
}
g.AddMount(sysMnt)
} else if !config.UsernsMode.IsHost() && config.NetMode.IsHost() {
addCgroup = false
g.RemoveMount("/sys")
sysMnt := spec.Mount{
Destination: "/sys",
Type: "bind",
Source: "/sys",
Options: []string{"nosuid", "noexec", "nodev", "ro", "rbind"},
}
g.AddMount(sysMnt)
}
if addCgroup {
cgroupMnt := spec.Mount{
Destination: "/sys/fs/cgroup",
Type: "cgroup",
Source: "cgroup",
Options: []string{"nosuid", "noexec", "nodev", "relatime", cgroupPerm},
}
g.AddMount(cgroupMnt)
}
g.SetProcessCwd(config.WorkDir)
g.SetProcessArgs(config.Command)
g.SetProcessTerminal(config.Tty)
for key, val := range config.GetAnnotations() {
g.AddAnnotation(key, val)
}
g.SetRootReadonly(config.ReadOnlyRootfs)
g.SetHostname(config.Hostname)
if config.Hostname != "" {
g.AddProcessEnv("HOSTNAME", config.Hostname)
}
for sysctlKey, sysctlVal := range config.Sysctl {
g.AddLinuxSysctl(sysctlKey, sysctlVal)
}
g.AddProcessEnv("container", "podman")
// RESOURCES - MEMORY
if config.Resources.Memory != 0 {
g.SetLinuxResourcesMemoryLimit(config.Resources.Memory)
}
if config.Resources.MemoryReservation != 0 {
g.SetLinuxResourcesMemoryReservation(config.Resources.MemoryReservation)
}
if config.Resources.MemorySwap != 0 {
g.SetLinuxResourcesMemorySwap(config.Resources.MemorySwap)
}
if config.Resources.KernelMemory != 0 {
g.SetLinuxResourcesMemoryKernel(config.Resources.KernelMemory)
}
if config.Resources.MemorySwappiness != -1 {
g.SetLinuxResourcesMemorySwappiness(uint64(config.Resources.MemorySwappiness))
}
g.SetLinuxResourcesMemoryDisableOOMKiller(config.Resources.DisableOomKiller)
g.SetProcessOOMScoreAdj(config.Resources.OomScoreAdj)
// RESOURCES - CPU
if config.Resources.CPUShares != 0 {
g.SetLinuxResourcesCPUShares(config.Resources.CPUShares)
}
if config.Resources.CPUQuota != 0 {
g.SetLinuxResourcesCPUQuota(config.Resources.CPUQuota)
}
if config.Resources.CPUPeriod != 0 {
g.SetLinuxResourcesCPUPeriod(config.Resources.CPUPeriod)
}
if config.Resources.CPUs != 0 {
g.SetLinuxResourcesCPUPeriod(cpuPeriod)
g.SetLinuxResourcesCPUQuota(int64(config.Resources.CPUs * cpuPeriod))
}
if config.Resources.CPURtRuntime != 0 {
g.SetLinuxResourcesCPURealtimeRuntime(config.Resources.CPURtRuntime)
}
if config.Resources.CPURtPeriod != 0 {
g.SetLinuxResourcesCPURealtimePeriod(config.Resources.CPURtPeriod)
}
if config.Resources.CPUsetCPUs != "" {
g.SetLinuxResourcesCPUCpus(config.Resources.CPUsetCPUs)
}
if config.Resources.CPUsetMems != "" {
g.SetLinuxResourcesCPUMems(config.Resources.CPUsetMems)
}
// Devices
if config.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.
if err := config.AddPrivilegedDevices(&g); err != nil {
return nil, err
}
} else {
for _, device := range config.Devices {
if err := addDevice(&g, device); err != nil {
return nil, err
}
}
}
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.SetProcessApparmorProfile(config.ApparmorProfile)
g.SetProcessSelinuxLabel(config.ProcessLabel)
g.SetLinuxMountLabel(config.MountLabel)
blockAccessToKernelFilesystems(config, &g)
// RESOURCES - PIDS
if config.Resources.PidsLimit != 0 {
g.SetLinuxResourcesPidsLimit(config.Resources.PidsLimit)
}
for _, i := range config.Tmpfs {
// Default options if nothing passed
options := []string{"rw", "noexec", "nosuid", "nodev", "size=65536k"}
spliti := strings.SplitN(i, ":", 2)
if len(spliti) > 1 {
if _, _, err := mount.ParseTmpfsOptions(spliti[1]); err != nil {
return nil, err
}
options = strings.Split(spliti[1], ",")
}
tmpfsMnt := spec.Mount{
Destination: spliti[0],
Type: "tmpfs",
Source: "tmpfs",
Options: append(options, "tmpcopyup"),
}
g.AddMount(tmpfsMnt)
}
for name, val := range config.Env {
g.AddProcessEnv(name, val)
}
if err := addRlimits(config, &g); err != nil {
return nil, err
}
if err := addPidNS(config, &g); err != nil {
return nil, err
}
if err := addUserNS(config, &g); err != nil {
return nil, err
}
if err := addNetNS(config, &g); err != nil {
return nil, err
}
if err := addUTSNS(config, &g); err != nil {
return nil, err
}
if err := addIpcNS(config, &g); err != nil {
return nil, err
}
configSpec := g.Spec()
// 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" {
if config.SeccompProfilePath != "" {
seccompProfile, err := ioutil.ReadFile(config.SeccompProfilePath)
if err != nil {
return nil, errors.Wrapf(err, "opening seccomp profile (%s) failed", config.SeccompProfilePath)
}
seccompConfig, err := seccomp.LoadProfile(string(seccompProfile), configSpec)
if err != nil {
return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath)
}
configSpec.Linux.Seccomp = seccompConfig
} else {
seccompConfig, err := seccomp.GetDefaultProfile(configSpec)
if err != nil {
return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath)
}
configSpec.Linux.Seccomp = seccompConfig
}
}
// Clear default Seccomp profile from Generator for privileged containers
if config.SeccompProfilePath == "unconfined" || config.Privileged {
configSpec.Linux.Seccomp = nil
}
// BIND MOUNTS
mounts, err := config.GetVolumeMounts(configSpec.Mounts)
if err != nil {
return nil, errors.Wrapf(err, "error getting volume mounts")
}
configSpec.Mounts = append(configSpec.Mounts, mounts...)
for _, mount := range configSpec.Mounts {
for _, opt := range mount.Options {
switch opt {
case "private", "rprivate", "slave", "rslave", "shared", "rshared":
if err := g.SetLinuxRootPropagation(opt); err != nil {
return nil, errors.Wrapf(err, "error setting root propagation for %q", mount.Destination)
}
}
}
}
// BLOCK IO
blkio, err := config.CreateBlockIO()
if err != nil {
return nil, errors.Wrapf(err, "error creating block io")
}
if blkio != nil {
configSpec.Linux.Resources.BlockIO = blkio
}
/*
//Annotations
Resources: &configSpec.LinuxResources{
BlockIO: &blkio,
//HugepageLimits:
Network: &configSpec.LinuxNetwork{
// ClassID *uint32
// Priorites []LinuxInterfacePriority
},
},
//CgroupsPath:
//Namespaces: []LinuxNamespace
// DefaultAction:
// Architectures
// Syscalls:
},
// RootfsPropagation
// MaskedPaths
// ReadonlyPaths:
// IntelRdt
},
}
*/
return configSpec, nil
}
func blockAccessToKernelFilesystems(config *CreateConfig, g *generate.Generator) {
if !config.Privileged {
for _, mp := range []string{
"/proc/kcore",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware",
} {
g.AddLinuxMaskedPaths(mp)
}
for _, rp := range []string{
"/proc/asound",
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger",
} {
g.AddLinuxReadonlyPaths(rp)
}
}
}
func addPidNS(config *CreateConfig, g *generate.Generator) error {
pidMode := config.PidMode
if pidMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.PIDNamespace))
}
if pidMode.IsContainer() {
logrus.Debug("using container pidmode")
}
return nil
}
func addUserNS(config *CreateConfig, g *generate.Generator) error {
if (len(config.IDMappings.UIDMap) > 0 || len(config.IDMappings.GIDMap) > 0) && !config.UsernsMode.IsHost() {
g.AddOrReplaceLinuxNamespace(spec.UserNamespace, "")
}
return nil
}
func addNetNS(config *CreateConfig, g *generate.Generator) error {
netMode := config.NetMode
if netMode.IsHost() {
logrus.Debug("Using host netmode")
return g.RemoveLinuxNamespace(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 netMode.IsContainer() {
logrus.Debug("Using container netmode")
} else {
return errors.Errorf("unknown network mode")
}
return nil
}
func addUTSNS(config *CreateConfig, g *generate.Generator) error {
utsMode := config.UtsMode
if utsMode.IsHost() {
return g.RemoveLinuxNamespace(spec.UTSNamespace)
}
return nil
}
func addIpcNS(config *CreateConfig, g *generate.Generator) error {
ipcMode := config.IpcMode
if ipcMode.IsHost() {
return g.RemoveLinuxNamespace(spec.IPCNamespace)
}
if ipcMode.IsContainer() {
logrus.Debug("Using container ipcmode")
}
return nil
}
func addRlimits(config *CreateConfig, g *generate.Generator) error {
var (
ul *units.Ulimit
err error
)
for _, u := range config.Resources.Ulimit {
if ul, err = units.ParseUlimit(u); err != nil {
return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u)
}
g.AddProcessRlimits("RLIMIT_"+strings.ToUpper(ul.Name), uint64(ul.Hard), uint64(ul.Soft))
}
return nil
}
func setupCapabilities(config *CreateConfig, configSpec *spec.Spec) error {
var err error
var caplist []string
caplist, err = caps.TweakCapabilities(configSpec.Process.Capabilities.Bounding, config.CapAdd, config.CapDrop)
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
return nil
}
func addDevice(g *generate.Generator, device string) error {
dev, err := devices.DeviceFromPath(device, "rwm")
if err != nil {
return errors.Wrapf(err, "%s is not a valid device", device)
}
linuxdev := spec.LinuxDevice{
Path: dev.Path,
Type: string(dev.Type),
Major: dev.Major,
Minor: dev.Minor,
FileMode: &dev.FileMode,
UID: &dev.Uid,
GID: &dev.Gid,
}
g.AddDevice(linuxdev)
g.AddLinuxResourcesDevice(true, string(dev.Type), &dev.Major, &dev.Minor, dev.Permissions)
return nil
}

View File

@ -1,4 +1,4 @@
package main package createconfig
import ( import (
"reflect" "reflect"
@ -15,7 +15,7 @@ func TestCreateConfig_GetVolumeMounts(t *testing.T) {
Source: "foobar", Source: "foobar",
Options: []string{"ro", "rbind", "private"}, Options: []string{"ro", "rbind", "private"},
} }
config := createConfig{ config := CreateConfig{
Volumes: []string{"foobar:/foobar:ro"}, Volumes: []string{"foobar:/foobar:ro"},
} }
specMount, err := config.GetVolumeMounts([]spec.Mount{}) specMount, err := config.GetVolumeMounts([]spec.Mount{})
@ -24,7 +24,7 @@ func TestCreateConfig_GetVolumeMounts(t *testing.T) {
} }
func TestCreateConfig_GetAnnotations(t *testing.T) { func TestCreateConfig_GetAnnotations(t *testing.T) {
config := createConfig{} config := CreateConfig{}
annotations := config.GetAnnotations() annotations := config.GetAnnotations()
assert.True(t, reflect.DeepEqual("sandbox", annotations["io.kubernetes.cri-o.ContainerType"])) assert.True(t, reflect.DeepEqual("sandbox", annotations["io.kubernetes.cri-o.ContainerType"]))
} }
@ -36,7 +36,7 @@ func TestCreateConfig_GetTmpfsMounts(t *testing.T) {
Source: "tmpfs", Source: "tmpfs",
Options: []string{"rw", "size=787448k", "mode=1777"}, Options: []string{"rw", "size=787448k", "mode=1777"},
} }
config := createConfig{ config := CreateConfig{
Tmpfs: []string{"/homer:rw,size=787448k,mode=1777"}, Tmpfs: []string{"/homer:rw,size=787448k,mode=1777"},
} }
tmpfsMount := config.GetTmpfsMounts() tmpfsMount := config.GetTmpfsMounts()

View File

@ -67,11 +67,6 @@ func (i *LibpodAPI) GetContainer(call ioprojectatomicpodman.VarlinkCall, name st
return call.ReplyGetContainer(makeListContainer(ctr.ID(), batchInfo)) return call.ReplyGetContainer(makeListContainer(ctr.ID(), batchInfo))
} }
// CreateContainer ...
func (i *LibpodAPI) CreateContainer(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("CreateContainer")
}
// InspectContainer ... // InspectContainer ...
func (i *LibpodAPI) InspectContainer(call ioprojectatomicpodman.VarlinkCall, name string) error { func (i *LibpodAPI) InspectContainer(call ioprojectatomicpodman.VarlinkCall, name string) error {
runtime, err := libpodruntime.GetRuntime(i.Cli) runtime, err := libpodruntime.GetRuntime(i.Cli)
@ -264,8 +259,26 @@ func (i *LibpodAPI) ResizeContainerTty(call ioprojectatomicpodman.VarlinkCall) e
} }
// StartContainer ... // StartContainer ...
func (i *LibpodAPI) StartContainer(call ioprojectatomicpodman.VarlinkCall) error { func (i *LibpodAPI) StartContainer(call ioprojectatomicpodman.VarlinkCall, name string) error {
return call.ReplyMethodNotImplemented("StartContainer") runtime, err := libpodruntime.GetRuntime(i.Cli)
if err != nil {
return call.ReplyRuntimeError(err.Error())
}
ctr, err := runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name)
}
state, err := ctr.State()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
if state == libpod.ContainerStateRunning || state == libpod.ContainerStatePaused {
return call.ReplyErrorOccurred("container is alrady running or paused")
}
if err := ctr.Start(getContext()); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyStartContainer(ctr.ID())
} }
// StopContainer ... // StopContainer ...
@ -429,3 +442,21 @@ func (i *LibpodAPI) DeleteStoppedContainers(call ioprojectatomicpodman.VarlinkCa
} }
return call.ReplyDeleteStoppedContainers(deletedContainers) return call.ReplyDeleteStoppedContainers(deletedContainers)
} }
// GetAttachSockets ...
func (i *LibpodAPI) GetAttachSockets(call ioprojectatomicpodman.VarlinkCall, name string) error {
runtime, err := libpodruntime.GetRuntime(i.Cli)
if err != nil {
return call.ReplyRuntimeError(err.Error())
}
ctr, err := runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name)
}
s := ioprojectatomicpodman.Sockets{
Container_id: ctr.ID(),
Io_socket: ctr.AttachSocketPath(),
Control_socket: ctr.ControlSocketPath(),
}
return call.ReplyGetAttachSockets(s)
}

View File

@ -0,0 +1,243 @@
package varlinkapi
import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
"syscall"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/signal"
"github.com/projectatomic/libpod/cmd/podman/libpodruntime"
"github.com/projectatomic/libpod/cmd/podman/varlink"
"github.com/projectatomic/libpod/libpod"
"github.com/projectatomic/libpod/libpod/image"
"github.com/projectatomic/libpod/pkg/inspect"
cc "github.com/projectatomic/libpod/pkg/spec"
"github.com/projectatomic/libpod/pkg/util"
"github.com/sirupsen/logrus"
)
// CreateContainer ...
func (i *LibpodAPI) CreateContainer(call ioprojectatomicpodman.VarlinkCall, config ioprojectatomicpodman.Create) error {
//mappings, err := util.ParseIDMapping(config.Uidmap, config.Gidmap, config.Subuidmap, config.Subgidmap)
//if err != nil {
// return err
//}
//storageOpts := storage.DefaultStoreOptions
//storageOpts.UIDMap = mappings.UIDMap
//storageOpts.GIDMap = mappings.GIDMap
runtime, err := libpodruntime.GetRuntime(i.Cli)
if err != nil {
return call.ReplyRuntimeError(err.Error())
}
defer runtime.Shutdown(false)
rtc := runtime.GetConfig()
ctx := getContext()
newImage, err := runtime.ImageRuntime().New(ctx, config.Image, rtc.SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{}, false, false)
if err != nil {
return err
}
data, err := newImage.Inspect(ctx)
createConfig, err := varlinkCreateToCreateConfig(ctx, config, runtime, config.Image, data)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
useImageVolumes := createConfig.ImageVolumeType == "bind"
runtimeSpec, err := cc.CreateConfigToOCISpec(createConfig)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
options, err := createConfig.GetContainerCreateOptions()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
// Gather up the options for NewContainer which consist of With... funcs
options = append(options, libpod.WithRootFSFromImage(createConfig.ImageID, createConfig.Image, useImageVolumes))
options = append(options, libpod.WithSELinuxLabels(createConfig.ProcessLabel, createConfig.MountLabel))
options = append(options, libpod.WithConmonPidFile(createConfig.ConmonPidFile))
options = append(options, libpod.WithLabels(createConfig.Labels))
options = append(options, libpod.WithUser(createConfig.User))
options = append(options, libpod.WithShmDir(createConfig.ShmDir))
options = append(options, libpod.WithShmSize(createConfig.Resources.ShmSize))
options = append(options, libpod.WithGroups(createConfig.GroupAdd))
options = append(options, libpod.WithIDMappings(*createConfig.IDMappings))
ctr, err := runtime.NewContainer(ctx, runtimeSpec, options...)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
createConfigJSON, err := json.Marshal(createConfig)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
if err := ctr.AddArtifact("create-config", createConfigJSON); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
logrus.Debug("new container created ", ctr.ID())
return call.ReplyCreateContainer(ctr.ID())
}
// varlinkCreateToCreateConfig takes the varlink input struct and maps it to a pointer
// of a CreateConfig, which eventually can be used to create the OCI spec.
func varlinkCreateToCreateConfig(ctx context.Context, create ioprojectatomicpodman.Create, runtime *libpod.Runtime, imageName string, data *inspect.ImageData) (*cc.CreateConfig, error) {
var (
inputCommand, command []string
memoryLimit, memoryReservation, memorySwap, memoryKernel int64
blkioWeight uint16
)
idmappings, err := util.ParseIDMapping(create.Uidmap, create.Gidmap, create.Subuidname, create.Subgidname)
if err != nil {
return nil, err
}
inputCommand = create.Command
entrypoint := create.Entrypoint
// ENTRYPOINT
// User input entrypoint takes priority over image entrypoint
if len(entrypoint) == 0 {
entrypoint = data.ContainerConfig.Entrypoint
}
// if entrypoint=, we need to clear the entrypoint
if len(entrypoint) == 1 && strings.Join(create.Entrypoint, "") == "" {
entrypoint = []string{}
}
// Build the command
// If we have an entry point, it goes first
if len(entrypoint) > 0 {
command = entrypoint
}
if len(inputCommand) > 0 {
// User command overrides data CMD
command = append(command, inputCommand...)
} else if len(data.ContainerConfig.Cmd) > 0 && len(create.Entrypoint) > 0 {
// If not user command, add CMD
command = append(command, data.ContainerConfig.Cmd...)
}
if create.Resources.Blkio_weight != 0 {
blkioWeight = uint16(create.Resources.Blkio_weight)
}
stopSignal := syscall.SIGTERM
if create.Stop_signal > 0 {
stopSignal, err = signal.ParseSignal(fmt.Sprintf("%d", create.Stop_signal))
if err != nil {
return nil, err
}
}
user := create.User
if user == "" {
user = data.ContainerConfig.User
}
// EXPOSED PORTS
portBindings, err := cc.ExposedPorts(create.Exposed_ports, create.Publish, create.Publish_all, data.ContainerConfig.ExposedPorts)
if err != nil {
return nil, err
}
// NETWORK MODE
networkMode := create.Net_mode
if networkMode == "" {
networkMode = "bridge"
}
// WORKING DIR
workDir := create.Work_dir
if workDir == "" {
workDir = "/"
}
imageID := data.ID
config := &cc.CreateConfig{
Runtime: runtime,
BuiltinImgVolumes: data.ContainerConfig.Volumes,
ConmonPidFile: create.Conmon_pidfile,
ImageVolumeType: create.Image_volume_type,
CapAdd: create.Cap_add,
CapDrop: create.Cap_drop,
CgroupParent: create.Cgroup_parent,
Command: command,
Detach: create.Detach,
Devices: create.Devices,
DNSOpt: create.Dns_opt,
DNSSearch: create.Dns_search,
DNSServers: create.Dns_servers,
Entrypoint: create.Entrypoint,
Env: create.Env,
GroupAdd: create.Group_add,
Hostname: create.Hostname,
HostAdd: create.Host_add,
IDMappings: idmappings,
Image: imageName,
ImageID: imageID,
Interactive: create.Interactive,
Labels: create.Labels,
LogDriver: create.Log_driver,
LogDriverOpt: create.Log_driver_opt,
Name: create.Name,
Network: networkMode,
IpcMode: container.IpcMode(create.Ipc_mode),
NetMode: container.NetworkMode(networkMode),
UtsMode: container.UTSMode(create.Uts_mode),
PidMode: container.PidMode(create.Pid_mode),
Pod: create.Pod,
Privileged: create.Privileged,
Publish: create.Publish,
PublishAll: create.Publish_all,
PortBindings: portBindings,
Quiet: create.Quiet,
ReadOnlyRootfs: create.Readonly_rootfs,
Resources: cc.CreateResourceConfig{
BlkioWeight: blkioWeight,
BlkioWeightDevice: create.Resources.Blkio_weight_device,
CPUShares: uint64(create.Resources.Cpu_shares),
CPUPeriod: uint64(create.Resources.Cpu_period),
CPUsetCPUs: create.Resources.Cpuset_cpus,
CPUsetMems: create.Resources.Cpuset_mems,
CPUQuota: create.Resources.Cpu_quota,
CPURtPeriod: uint64(create.Resources.Cpu_rt_period),
CPURtRuntime: create.Resources.Cpu_rt_runtime,
CPUs: create.Resources.Cpus,
DeviceReadBps: create.Resources.Device_read_bps,
DeviceReadIOps: create.Resources.Device_write_bps,
DeviceWriteBps: create.Resources.Device_read_iops,
DeviceWriteIOps: create.Resources.Device_write_iops,
DisableOomKiller: create.Resources.Disable_oomkiller,
ShmSize: create.Resources.Shm_size,
Memory: memoryLimit,
MemoryReservation: memoryReservation,
MemorySwap: memorySwap,
MemorySwappiness: int(create.Resources.Memory_swappiness),
KernelMemory: memoryKernel,
OomScoreAdj: int(create.Resources.Oom_score_adj),
PidsLimit: create.Resources.Pids_limit,
Ulimit: create.Resources.Ulimit,
},
Rm: create.Rm,
ShmDir: create.Shm_dir,
StopSignal: stopSignal,
StopTimeout: uint(create.Stop_timeout),
Sysctl: create.Sys_ctl,
Tmpfs: create.Tmpfs,
Tty: create.Tty,
User: user,
UsernsMode: container.UsernsMode(create.Userns_mode),
Volumes: create.Volumes,
WorkDir: workDir,
}
return config, nil
}