Adding parsing functionality for various specific create/run options
Signed-off-by: umohnani8 <umohnani@redhat.com>
This commit is contained in:
parent
fb3f5b6b9a
commit
13d8338c68
3 changed files with 966 additions and 142 deletions
|
@ -3,39 +3,39 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"strings"
|
||||||
|
|
||||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
"strings"
|
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type createResourceConfig struct {
|
type createResourceConfig struct {
|
||||||
blkioWeight int64 // blkio-weight
|
blkioWeight int64 // blkio-weight
|
||||||
blkioDevice []string // blkio-weight
|
blkioDevice []string // blkio-weight
|
||||||
cpuShares int64 // cpu-shares
|
cpuShares int64 // cpu-shares
|
||||||
cpuCount int64 // cpu-count
|
cpuCount int64 // cpu-count
|
||||||
cpuPeriod int64 // cpu-period
|
cpuPeriod int64 // cpu-period
|
||||||
cpusetCpus string
|
cpusetCpus string
|
||||||
cpusetNames string
|
cpusetNames string
|
||||||
cpuFile string
|
cpuFile string
|
||||||
cpuMems string // cpuset-mems
|
cpuMems string // cpuset-mems
|
||||||
cpuQuota int64 // cpu-quota
|
cpuQuota int64 // cpu-quota
|
||||||
cpuRtPeriod int64 // cpu-rt-period
|
cpuRtPeriod int64 // cpu-rt-period
|
||||||
cpuRtRuntime int64 // cpu-rt-runtime
|
cpuRtRuntime int64 // cpu-rt-runtime
|
||||||
cpus int64 // cpus
|
cpus int64 // cpus
|
||||||
deviceReadBps []string // device-read-bps
|
deviceReadBps []string // device-read-bps
|
||||||
deviceReadIops []string // device-read-iops
|
deviceReadIops []string // device-read-iops
|
||||||
deviceWriteBps []string // device-write-bps
|
deviceWriteBps []string // device-write-bps
|
||||||
deviceWriteIops []string // device-write-iops
|
deviceWriteIops []string // device-write-iops
|
||||||
memory string //memory
|
memory string //memory
|
||||||
memoryReservation string // memory-reservation
|
memoryReservation string // memory-reservation
|
||||||
memorySwap string //memory-swap
|
memorySwap string //memory-swap
|
||||||
memorySwapiness string // memory-swappiness
|
memorySwapiness string // memory-swappiness
|
||||||
kernelMemory string // kernel-memory
|
kernelMemory string // kernel-memory
|
||||||
oomScoreAdj string //oom-score-adj
|
oomScoreAdj string //oom-score-adj
|
||||||
pidsLimit string // pids-limit
|
pidsLimit string // pids-limit
|
||||||
shmSize string
|
shmSize string
|
||||||
ulimit []string //ulimit
|
ulimit []string //ulimit
|
||||||
}
|
}
|
||||||
|
@ -45,58 +45,58 @@ type createConfig struct {
|
||||||
args []string
|
args []string
|
||||||
capAdd []string // cap-add
|
capAdd []string // cap-add
|
||||||
capDrop []string // cap-drop
|
capDrop []string // cap-drop
|
||||||
cgroupParent string // cgroup-parent
|
cgroupParent string // cgroup-parent
|
||||||
command []string
|
command []string
|
||||||
detach bool // detach
|
detach bool // detach
|
||||||
devices []*pb.Device // device
|
devices []*pb.Device // device
|
||||||
dnsOpt []string //dns-opt
|
dnsOpt []string //dns-opt
|
||||||
dnsSearch []string //dns-search
|
dnsSearch []string //dns-search
|
||||||
dnsServers []string //dns
|
dnsServers []string //dns
|
||||||
entrypoint string //entrypoint
|
entrypoint string //entrypoint
|
||||||
env []string //env
|
env []string //env
|
||||||
expose []string //expose
|
expose []string //expose
|
||||||
groupAdd []string // group-add
|
groupAdd []string // group-add
|
||||||
hostname string //hostname
|
hostname string //hostname
|
||||||
image string
|
image string
|
||||||
interactive bool //interactive
|
interactive bool //interactive
|
||||||
ip6Address string //ipv6
|
ip6Address string //ipv6
|
||||||
ipAddress string //ip
|
ipAddress string //ip
|
||||||
labels map[string]string //label
|
labels map[string]string //label
|
||||||
linkLocalIP []string // link-local-ip
|
linkLocalIP []string // link-local-ip
|
||||||
logDriver string // log-driver
|
logDriver string // log-driver
|
||||||
logDriverOpt []string // log-opt
|
logDriverOpt []string // log-opt
|
||||||
macAddress string //mac-address
|
macAddress string //mac-address
|
||||||
mounts []*pb.Mount
|
mounts []*pb.Mount
|
||||||
name string //name
|
name string //name
|
||||||
network string //network
|
network string //network
|
||||||
networkAlias []string //network-alias
|
networkAlias []string //network-alias
|
||||||
nsIPC string // ipc
|
nsIPC string // ipc
|
||||||
nsNet string //net
|
nsNet string //net
|
||||||
nsPID string //pid
|
nsPID string //pid
|
||||||
nsUser string
|
nsUser string
|
||||||
pod string //pod
|
pod string //pod
|
||||||
ports []*pb.PortMapping
|
ports []*pb.PortMapping
|
||||||
privileged bool //privileged
|
privileged bool //privileged
|
||||||
publish []string //publish
|
publish []string //publish
|
||||||
publishAll bool //publish-all
|
publishAll bool //publish-all
|
||||||
readOnlyRootfs bool //read-only
|
readOnlyRootfs bool //read-only
|
||||||
resources createResourceConfig
|
resources createResourceConfig
|
||||||
rm bool //rm
|
rm bool //rm
|
||||||
securityOpts []string //security-opt
|
securityOpts []string //security-opt
|
||||||
shmSize string //shm-size
|
shmSize string //shm-size
|
||||||
sigProxy bool //sig-proxy
|
sigProxy bool //sig-proxy
|
||||||
stdin bool
|
stdin bool
|
||||||
stopSignal string // stop-signal
|
stopSignal string // stop-signal
|
||||||
stopTimeout int64 // stop-timeout
|
stopTimeout int64 // stop-timeout
|
||||||
storageOpts []string //storage-opt
|
storageOpts []string //storage-opt
|
||||||
sysctl map[string]string //sysctl
|
sysctl map[string]string //sysctl
|
||||||
tmpfs []string // tmpfs
|
tmpfs []string // tmpfs
|
||||||
tty bool //tty
|
tty bool //tty
|
||||||
user int64 //user
|
user int64 //user
|
||||||
userns string //userns
|
userns string //userns
|
||||||
volumes []string //volume
|
volumes []string //volume
|
||||||
volumesFrom []string //volumes-from
|
volumesFrom []string //volumes-from
|
||||||
workDir string //workdir
|
workDir string //workdir
|
||||||
}
|
}
|
||||||
|
|
||||||
var createDescription = "Creates a new container from the given image or" +
|
var createDescription = "Creates a new container from the given image or" +
|
||||||
|
@ -114,7 +114,7 @@ var createCommand = cli.Command{
|
||||||
ArgsUsage: "IMAGE [COMMAND [ARG...]]",
|
ArgsUsage: "IMAGE [COMMAND [ARG...]]",
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyImage(image string) bool{
|
func verifyImage(image string) bool {
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -174,15 +174,15 @@ func parseCreateOpts(c *cli.Context) (*createConfig, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.StringSlice("env")) > 0 {
|
if len(c.StringSlice("env")) > 0 {
|
||||||
for _, inputEnv := range(c.StringSlice("env")) {
|
for _, inputEnv := range c.StringSlice("env") {
|
||||||
env = append(env, inputEnv)
|
env = append(env, inputEnv)
|
||||||
}
|
}
|
||||||
} else{
|
} else {
|
||||||
env = append(env, "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "TERM=xterm")
|
env = append(env, "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "TERM=xterm")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.StringSlice("sysctl")) > 0 {
|
if len(c.StringSlice("sysctl")) > 0 {
|
||||||
for _, inputSysctl := range(c.StringSlice("sysctl")) {
|
for _, inputSysctl := range c.StringSlice("sysctl") {
|
||||||
values := strings.Split(inputSysctl, "=")
|
values := strings.Split(inputSysctl, "=")
|
||||||
sysctl[values[0]] = values[1]
|
sysctl[values[0]] = values[1]
|
||||||
}
|
}
|
||||||
|
@ -194,79 +194,79 @@ func parseCreateOpts(c *cli.Context) (*createConfig, error) {
|
||||||
image := c.Args()[0]
|
image := c.Args()[0]
|
||||||
|
|
||||||
config := &createConfig{
|
config := &createConfig{
|
||||||
capAdd: c.StringSlice("cap-add"),
|
capAdd: c.StringSlice("cap-add"),
|
||||||
capDrop: c.StringSlice("cap-drop"),
|
capDrop: c.StringSlice("cap-drop"),
|
||||||
cgroupParent: c.String("cgroup-parent"),
|
cgroupParent: c.String("cgroup-parent"),
|
||||||
command: command,
|
command: command,
|
||||||
detach: c.Bool("detach"),
|
detach: c.Bool("detach"),
|
||||||
dnsOpt: c.StringSlice("dns-opt"),
|
dnsOpt: c.StringSlice("dns-opt"),
|
||||||
dnsSearch: c.StringSlice("dns-search"),
|
dnsSearch: c.StringSlice("dns-search"),
|
||||||
dnsServers: c.StringSlice("dns"),
|
dnsServers: c.StringSlice("dns"),
|
||||||
entrypoint: c.String("entrypoint"),
|
entrypoint: c.String("entrypoint"),
|
||||||
env: env,
|
env: env,
|
||||||
expose: c.StringSlice("env"),
|
expose: c.StringSlice("env"),
|
||||||
groupAdd:c.StringSlice("group-add"),
|
groupAdd: c.StringSlice("group-add"),
|
||||||
hostname: c.String("hostname"),
|
hostname: c.String("hostname"),
|
||||||
image: image,
|
image: image,
|
||||||
interactive: c.Bool("interactive"),
|
interactive: c.Bool("interactive"),
|
||||||
ip6Address: c.String("ipv6"),
|
ip6Address: c.String("ipv6"),
|
||||||
ipAddress: c.String("ip"),
|
ipAddress: c.String("ip"),
|
||||||
labels: labels,
|
labels: labels,
|
||||||
linkLocalIP: c.StringSlice("link-local-ip"),
|
linkLocalIP: c.StringSlice("link-local-ip"),
|
||||||
logDriver: c.String("log-driver"),
|
logDriver: c.String("log-driver"),
|
||||||
logDriverOpt: c.StringSlice("log-opt"),
|
logDriverOpt: c.StringSlice("log-opt"),
|
||||||
macAddress:c.String("mac-address"),
|
macAddress: c.String("mac-address"),
|
||||||
name: c.String("name"),
|
name: c.String("name"),
|
||||||
network: c.String("network"),
|
network: c.String("network"),
|
||||||
networkAlias: c.StringSlice("network-alias"),
|
networkAlias: c.StringSlice("network-alias"),
|
||||||
nsIPC: c.String("ipc"),
|
nsIPC: c.String("ipc"),
|
||||||
nsNet: c.String("net"),
|
nsNet: c.String("net"),
|
||||||
nsPID: c.String("pid"),
|
nsPID: c.String("pid"),
|
||||||
pod: c.String("pod"),
|
pod: c.String("pod"),
|
||||||
privileged: c.Bool("privileged"),
|
privileged: c.Bool("privileged"),
|
||||||
publish: c.StringSlice("publish"),
|
publish: c.StringSlice("publish"),
|
||||||
publishAll: c.Bool("publish-all"),
|
publishAll: c.Bool("publish-all"),
|
||||||
readOnlyRootfs: c.Bool("read-only"),
|
readOnlyRootfs: c.Bool("read-only"),
|
||||||
resources:createResourceConfig{
|
resources: createResourceConfig{
|
||||||
blkioWeight:c.Int64("blkio-weight"),
|
blkioWeight: c.Int64("blkio-weight"),
|
||||||
blkioDevice: c.StringSlice("blkio-device"),
|
blkioDevice: c.StringSlice("blkio-device"),
|
||||||
cpuShares:c.Int64("cpu-shares"),
|
cpuShares: c.Int64("cpu-shares"),
|
||||||
cpuCount: c.Int64("cpu-count"),
|
cpuCount: c.Int64("cpu-count"),
|
||||||
cpuPeriod:c.Int64("cpu-period"),
|
cpuPeriod: c.Int64("cpu-period"),
|
||||||
cpusetCpus:c.String("cpu-period"),
|
cpusetCpus: c.String("cpu-period"),
|
||||||
cpuMems:c.String("cpuset-mems"),
|
cpuMems: c.String("cpuset-mems"),
|
||||||
cpuQuota: c.Int64("cpu-quota"),
|
cpuQuota: c.Int64("cpu-quota"),
|
||||||
cpuRtPeriod: c.Int64("cpu-rt-period"),
|
cpuRtPeriod: c.Int64("cpu-rt-period"),
|
||||||
cpuRtRuntime: c.Int64("cpu-rt-runtime"),
|
cpuRtRuntime: c.Int64("cpu-rt-runtime"),
|
||||||
cpus: c.Int64("cpus"),
|
cpus: c.Int64("cpus"),
|
||||||
deviceReadBps: c.StringSlice("device-read-bps"),
|
deviceReadBps: c.StringSlice("device-read-bps"),
|
||||||
deviceReadIops:c.StringSlice("device-read-iops"),
|
deviceReadIops: c.StringSlice("device-read-iops"),
|
||||||
deviceWriteBps:c.StringSlice("device-write-bps"),
|
deviceWriteBps: c.StringSlice("device-write-bps"),
|
||||||
deviceWriteIops:c.StringSlice("device-write-iops"),
|
deviceWriteIops: c.StringSlice("device-write-iops"),
|
||||||
memory: c.String("memory"),
|
memory: c.String("memory"),
|
||||||
memoryReservation: c.String("memory-reservation"),
|
memoryReservation: c.String("memory-reservation"),
|
||||||
memorySwap: c.String("memory-swap"),
|
memorySwap: c.String("memory-swap"),
|
||||||
memorySwapiness:c.String("memory-swapiness"),
|
memorySwapiness: c.String("memory-swapiness"),
|
||||||
kernelMemory: c.String("kernel-memory"),
|
kernelMemory: c.String("kernel-memory"),
|
||||||
oomScoreAdj:c.String("oom-score-adj"),
|
oomScoreAdj: c.String("oom-score-adj"),
|
||||||
pidsLimit: c.String("pids-limit"),
|
pidsLimit: c.String("pids-limit"),
|
||||||
ulimit:c.StringSlice("ulimit"),
|
ulimit: c.StringSlice("ulimit"),
|
||||||
},
|
},
|
||||||
rm: c.Bool("rm"),
|
rm: c.Bool("rm"),
|
||||||
securityOpts:c.StringSlice("security-opt"),
|
securityOpts: c.StringSlice("security-opt"),
|
||||||
shmSize: c.String("shm-size"),
|
shmSize: c.String("shm-size"),
|
||||||
sigProxy:c.Bool("sig-proxy"),
|
sigProxy: c.Bool("sig-proxy"),
|
||||||
stopSignal:c.String("stop-signal"),
|
stopSignal: c.String("stop-signal"),
|
||||||
stopTimeout:c.Int64("stop-timeout"),
|
stopTimeout: c.Int64("stop-timeout"),
|
||||||
storageOpts:c.StringSlice("storage-opt"),
|
storageOpts: c.StringSlice("storage-opt"),
|
||||||
sysctl: sysctl,
|
sysctl: sysctl,
|
||||||
tmpfs: c.StringSlice("tmpfs"),
|
tmpfs: c.StringSlice("tmpfs"),
|
||||||
tty: c.Bool("tty"), //
|
tty: c.Bool("tty"), //
|
||||||
user: c.Int64("user"),
|
user: c.Int64("user"),
|
||||||
userns: c.String("userns"),
|
userns: c.String("userns"),
|
||||||
volumes:c.StringSlice("volume"),
|
volumes: c.StringSlice("volume"),
|
||||||
volumesFrom:c.StringSlice("volumes-from"),
|
volumesFrom: c.StringSlice("volumes-from"),
|
||||||
workDir:c.String("workdir"),
|
workDir: c.String("workdir"),
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
|
@ -277,14 +277,11 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
|
||||||
spec := &spec.Spec{
|
spec := &spec.Spec{
|
||||||
Version: "1.0.0.0", // where do I get this?
|
Version: "1.0.0.0", // where do I get this?
|
||||||
Process: &spec.Process{
|
Process: &spec.Process{
|
||||||
Terminal: config.tty,
|
Terminal: config.tty,
|
||||||
User: spec.User{
|
User: spec.User{},
|
||||||
},
|
Args: config.command,
|
||||||
Args: config.command,
|
Env: config.env,
|
||||||
Env: config.env,
|
Capabilities: &spec.LinuxCapabilities{},
|
||||||
Capabilities: &spec.LinuxCapabilities{
|
|
||||||
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Root: &spec.Root{
|
Root: &spec.Root{
|
||||||
Readonly: config.readOnlyRootfs,
|
Readonly: config.readOnlyRootfs,
|
||||||
|
@ -296,15 +293,13 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
|
||||||
Linux: &spec.Linux{
|
Linux: &spec.Linux{
|
||||||
// UIDMappings
|
// UIDMappings
|
||||||
// GIDMappings
|
// GIDMappings
|
||||||
Sysctl: config.sysctl,
|
Sysctl: config.sysctl,
|
||||||
Resources: &spec.LinuxResources{
|
Resources: &spec.LinuxResources{
|
||||||
Devices: spec.LinuxDeviceCgroup,
|
// Devices: spec.LinuxDeviceCgroup,
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return spec, errors.Errorf("NOT IMPLEMENTED")
|
return spec, errors.Errorf("NOT IMPLEMENTED")
|
||||||
}
|
}
|
||||||
|
|
661
cmd/kpod/parse.go
Normal file
661
cmd/kpod/parse.go
Normal file
|
@ -0,0 +1,661 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
whiteSpaces = " \t"
|
||||||
|
alphaRegexp = regexp.MustCompile(`[a-zA-Z]`)
|
||||||
|
domainRegexp = regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// validateMACAddress validates a MAC address.
|
||||||
|
func validateMACAddress(val string) (string, error) {
|
||||||
|
_, err := net.ParseMAC(strings.TrimSpace(val))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateLink validates that the specified string has a valid link format (containerName:alias).
|
||||||
|
func validateLink(val string) (string, error) {
|
||||||
|
if _, _, err := parseLink(val); err != nil {
|
||||||
|
return val, err
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validDeviceMode checks if the mode for device is valid or not.
|
||||||
|
// Valid mode is a composition of r (read), w (write), and m (mknod).
|
||||||
|
func validDeviceMode(mode string) bool {
|
||||||
|
var legalDeviceMode = map[rune]bool{
|
||||||
|
'r': true,
|
||||||
|
'w': true,
|
||||||
|
'm': true,
|
||||||
|
}
|
||||||
|
if mode == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c := range mode {
|
||||||
|
if !legalDeviceMode[c] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
legalDeviceMode[c] = false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateDevice validates a path for devices
|
||||||
|
// It will make sure 'val' is in the form:
|
||||||
|
// [host-dir:]container-path[:mode]
|
||||||
|
// It also validates the device mode.
|
||||||
|
func validateDevice(val string) (string, error) {
|
||||||
|
return validatePath(val, validDeviceMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validatePath(val string, validator func(string) bool) (string, error) {
|
||||||
|
var containerPath string
|
||||||
|
var mode string
|
||||||
|
|
||||||
|
if strings.Count(val, ":") > 2 {
|
||||||
|
return val, fmt.Errorf("bad format for path: %s", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
split := strings.SplitN(val, ":", 3)
|
||||||
|
if split[0] == "" {
|
||||||
|
return val, fmt.Errorf("bad format for path: %s", val)
|
||||||
|
}
|
||||||
|
switch len(split) {
|
||||||
|
case 1:
|
||||||
|
containerPath = split[0]
|
||||||
|
val = path.Clean(containerPath)
|
||||||
|
case 2:
|
||||||
|
if isValid := validator(split[1]); isValid {
|
||||||
|
containerPath = split[0]
|
||||||
|
mode = split[1]
|
||||||
|
val = fmt.Sprintf("%s:%s", path.Clean(containerPath), mode)
|
||||||
|
} else {
|
||||||
|
containerPath = split[1]
|
||||||
|
val = fmt.Sprintf("%s:%s", split[0], path.Clean(containerPath))
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
containerPath = split[1]
|
||||||
|
mode = split[2]
|
||||||
|
if isValid := validator(split[2]); !isValid {
|
||||||
|
return val, fmt.Errorf("bad mode specified: %s", mode)
|
||||||
|
}
|
||||||
|
val = fmt.Sprintf("%s:%s:%s", split[0], containerPath, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !path.IsAbs(containerPath) {
|
||||||
|
return val, fmt.Errorf("%s is not an absolute path", containerPath)
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateProto(proto string) bool {
|
||||||
|
for _, availableProto := range []string{"tcp", "udp"} {
|
||||||
|
if availableProto == proto {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateAttach validates that the specified string is a valid attach option.
|
||||||
|
func validateAttach(val string) (string, error) {
|
||||||
|
s := strings.ToLower(val)
|
||||||
|
for _, str := range []string{"stdin", "stdout", "stderr"} {
|
||||||
|
if s == str {
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val, fmt.Errorf("valid streams are STDIN, STDOUT and STDERR")
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateEnv validates an environment variable and returns it.
|
||||||
|
// If no value is specified, it returns the current value using os.Getenv.
|
||||||
|
//
|
||||||
|
// As on ParseEnvFile and related to #16585, environment variable names
|
||||||
|
// are not validate what so ever, it's up to application inside docker
|
||||||
|
// to validate them or not.
|
||||||
|
func validateEnv(val string) (string, error) {
|
||||||
|
arr := strings.Split(val, "=")
|
||||||
|
if len(arr) > 1 {
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
if !doesEnvExist(val) {
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s=%s", val, os.Getenv(val)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func doesEnvExist(name string) bool {
|
||||||
|
for _, entry := range os.Environ() {
|
||||||
|
parts := strings.SplitN(entry, "=", 2)
|
||||||
|
if parts[0] == name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateExtraHost validates that the specified string is a valid extrahost and returns it.
|
||||||
|
// ExtraHost is in the form of name:ip where the ip has to be a valid ip (ipv4 or ipv6).
|
||||||
|
func validateExtraHost(val string) (string, error) {
|
||||||
|
// allow for IPv6 addresses in extra hosts by only splitting on first ":"
|
||||||
|
arr := strings.SplitN(val, ":", 2)
|
||||||
|
if len(arr) != 2 || len(arr[0]) == 0 {
|
||||||
|
return "", fmt.Errorf("bad format for add-host: %q", val)
|
||||||
|
}
|
||||||
|
if _, err := validateIPAddress(arr[1]); err != nil {
|
||||||
|
return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1])
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateIPAddress validates an Ip address.
|
||||||
|
func validateIPAddress(val string) (string, error) {
|
||||||
|
var ip = net.ParseIP(strings.TrimSpace(val))
|
||||||
|
if ip != nil {
|
||||||
|
return ip.String(), nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("%s is not an ip address", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateDNSSearch validates domain for resolvconf search configuration.
|
||||||
|
// A zero length domain is represented by a dot (.).
|
||||||
|
func validateDNSSearch(val string) (string, error) {
|
||||||
|
if val = strings.Trim(val, " "); val == "." {
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
return validateDomain(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateDomain(val string) (string, error) {
|
||||||
|
if alphaRegexp.FindString(val) == "" {
|
||||||
|
return "", fmt.Errorf("%s is not a valid domain", val)
|
||||||
|
}
|
||||||
|
ns := domainRegexp.FindSubmatch([]byte(val))
|
||||||
|
if len(ns) > 0 && len(ns[1]) < 255 {
|
||||||
|
return string(ns[1]), nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("%s is not a valid domain", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateLabel validates that the specified string is a valid label, and returns it.
|
||||||
|
// Labels are in the form on key=value.
|
||||||
|
func validateLabel(val string) (string, error) {
|
||||||
|
if strings.Count(val, "=") < 1 {
|
||||||
|
return "", fmt.Errorf("bad attribute format: %s", val)
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseDevice parses a device mapping string to a container.DeviceMapping struct
|
||||||
|
func parseDevice(device string) (*pb.Device, error) {
|
||||||
|
src := ""
|
||||||
|
dst := ""
|
||||||
|
permissions := "rwm"
|
||||||
|
arr := strings.Split(device, ":")
|
||||||
|
switch len(arr) {
|
||||||
|
case 3:
|
||||||
|
permissions = arr[2]
|
||||||
|
fallthrough
|
||||||
|
case 2:
|
||||||
|
if validDeviceMode(arr[1]) {
|
||||||
|
permissions = arr[1]
|
||||||
|
} else {
|
||||||
|
dst = arr[1]
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case 1:
|
||||||
|
src = arr[0]
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid device specification: %s", device)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dst == "" {
|
||||||
|
dst = src
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceMapping := &pb.Device{
|
||||||
|
ContainerPath: dst,
|
||||||
|
HostPath: src,
|
||||||
|
Permissions: permissions,
|
||||||
|
}
|
||||||
|
return deviceMapping, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseEnvFile reads a file with environment variables enumerated by lines
|
||||||
|
//
|
||||||
|
// ``Environment variable names used by the utilities in the Shell and
|
||||||
|
// Utilities volume of IEEE Std 1003.1-2001 consist solely of uppercase
|
||||||
|
// letters, digits, and the '_' (underscore) from the characters defined in
|
||||||
|
// Portable Character Set and do not begin with a digit. *But*, other
|
||||||
|
// characters may be permitted by an implementation; applications shall
|
||||||
|
// tolerate the presence of such names.''
|
||||||
|
// -- http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
|
||||||
|
//
|
||||||
|
// As of #16585, it's up to application inside docker to validate or not
|
||||||
|
// environment variables, that's why we just strip leading whitespace and
|
||||||
|
// nothing more.
|
||||||
|
func parseEnvFile(filename string) ([]string, error) {
|
||||||
|
fh, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, err
|
||||||
|
}
|
||||||
|
defer fh.Close()
|
||||||
|
|
||||||
|
lines := []string{}
|
||||||
|
scanner := bufio.NewScanner(fh)
|
||||||
|
for scanner.Scan() {
|
||||||
|
// trim the line from all leading whitespace first
|
||||||
|
line := strings.TrimLeft(scanner.Text(), whiteSpaces)
|
||||||
|
// line is not empty, and not starting with '#'
|
||||||
|
if len(line) > 0 && !strings.HasPrefix(line, "#") {
|
||||||
|
data := strings.SplitN(line, "=", 2)
|
||||||
|
|
||||||
|
// trim the front of a variable, but nothing else
|
||||||
|
variable := strings.TrimLeft(data[0], whiteSpaces)
|
||||||
|
if strings.ContainsAny(variable, whiteSpaces) {
|
||||||
|
return []string{}, errors.Errorf("variable %q has white spaces, poorly formatted environment", variable)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) > 1 {
|
||||||
|
|
||||||
|
// pass the value through, no trimming
|
||||||
|
lines = append(lines, fmt.Sprintf("%s=%s", variable, data[1]))
|
||||||
|
} else {
|
||||||
|
// if only a pass-through variable is given, clean it up.
|
||||||
|
lines = append(lines, fmt.Sprintf("%s=%s", strings.TrimSpace(line), os.Getenv(line)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lines, scanner.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseLink parses and validates the specified string as a link format (name:alias)
|
||||||
|
func parseLink(val string) (string, string, error) {
|
||||||
|
if val == "" {
|
||||||
|
return "", "", fmt.Errorf("empty string specified for links")
|
||||||
|
}
|
||||||
|
arr := strings.Split(val, ":")
|
||||||
|
if len(arr) > 2 {
|
||||||
|
return "", "", fmt.Errorf("bad format for links: %s", val)
|
||||||
|
}
|
||||||
|
if len(arr) == 1 {
|
||||||
|
return val, val, nil
|
||||||
|
}
|
||||||
|
// This is kept because we can actually get a HostConfig with links
|
||||||
|
// from an already created container and the format is not `foo:bar`
|
||||||
|
// but `/foo:/c1/bar`
|
||||||
|
if strings.HasPrefix(arr[0], "/") {
|
||||||
|
_, alias := path.Split(arr[1])
|
||||||
|
return arr[0][1:], alias, nil
|
||||||
|
}
|
||||||
|
return arr[0], arr[1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLoggingOpts(logDriver string, logDriverOpt []string) (map[string]string, error) {
|
||||||
|
logOptsMap := convertKVStringsToMap(logDriverOpt)
|
||||||
|
if logDriver == "none" && len(logDriverOpt) > 0 {
|
||||||
|
return map[string]string{}, errors.Errorf("invalid logging opts for driver %s", logDriver)
|
||||||
|
}
|
||||||
|
return logOptsMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// takes a local seccomp daemon, reads the file contents for sending to the daemon
|
||||||
|
func parseSecurityOpts(securityOpts []string) ([]string, error) {
|
||||||
|
for key, opt := range securityOpts {
|
||||||
|
con := strings.SplitN(opt, "=", 2)
|
||||||
|
if len(con) == 1 && con[0] != "no-new-privileges" {
|
||||||
|
if strings.Index(opt, ":") != -1 {
|
||||||
|
con = strings.SplitN(opt, ":", 2)
|
||||||
|
} else {
|
||||||
|
return securityOpts, fmt.Errorf("Invalid --security-opt: %q", opt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if con[0] == "seccomp" && con[1] != "unconfined" {
|
||||||
|
f, err := ioutil.ReadFile(con[1])
|
||||||
|
if err != nil {
|
||||||
|
return securityOpts, fmt.Errorf("opening seccomp profile (%s) failed: %v", con[1], err)
|
||||||
|
}
|
||||||
|
b := bytes.NewBuffer(nil)
|
||||||
|
if err := json.Compact(b, f); err != nil {
|
||||||
|
return securityOpts, fmt.Errorf("compacting json for seccomp profile (%s) failed: %v", con[1], err)
|
||||||
|
}
|
||||||
|
securityOpts[key] = fmt.Sprintf("seccomp=%s", b.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return securityOpts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parses storage options per container into a map
|
||||||
|
func parseStorageOpts(storageOpts []string) (map[string]string, error) {
|
||||||
|
m := make(map[string]string)
|
||||||
|
for _, option := range storageOpts {
|
||||||
|
if strings.Contains(option, "=") {
|
||||||
|
opt := strings.SplitN(option, "=", 2)
|
||||||
|
m[opt[0]] = opt[1]
|
||||||
|
} else {
|
||||||
|
return nil, errors.Errorf("invalid storage option %q", option)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsePortSpecs receives port specs in the format of ip:public:private/proto and parses
|
||||||
|
// these in to the internal types
|
||||||
|
func parsePortSpecs(ports []string) ([]*pb.PortMapping, error) {
|
||||||
|
var portMappings []*pb.PortMapping
|
||||||
|
for _, rawPort := range ports {
|
||||||
|
portMapping, err := parsePortSpec(rawPort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
portMappings = append(portMappings, portMapping...)
|
||||||
|
}
|
||||||
|
return portMappings, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsePortSpec parses a port specification string into a slice of PortMappings
|
||||||
|
func parsePortSpec(rawPort string) ([]*pb.PortMapping, error) {
|
||||||
|
var proto string
|
||||||
|
rawIP, hostPort, containerPort := splitParts(rawPort)
|
||||||
|
proto, containerPort = splitProtoPort(containerPort)
|
||||||
|
|
||||||
|
// Strip [] from IPV6 addresses
|
||||||
|
ip, _, err := net.SplitHostPort(rawIP + ":")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Invalid ip address %v: %s", rawIP, err)
|
||||||
|
}
|
||||||
|
if ip != "" && net.ParseIP(ip) == nil {
|
||||||
|
return nil, fmt.Errorf("Invalid ip address: %s", ip)
|
||||||
|
}
|
||||||
|
if containerPort == "" {
|
||||||
|
return nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
startPort, endPort, err := parsePortRange(containerPort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
var startHostPort, endHostPort uint64 = 0, 0
|
||||||
|
if len(hostPort) > 0 {
|
||||||
|
startHostPort, endHostPort, err = parsePortRange(hostPort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hostPort != "" && (endPort-startPort) != (endHostPort-startHostPort) {
|
||||||
|
// Allow host port range iff containerPort is not a range.
|
||||||
|
// In this case, use the host port range as the dynamic
|
||||||
|
// host port range to allocate into.
|
||||||
|
if endPort != startPort {
|
||||||
|
return nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !validateProto(strings.ToLower(proto)) {
|
||||||
|
return nil, fmt.Errorf("invalid proto: %s", proto)
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol := pb.Protocol_TCP
|
||||||
|
if strings.ToLower(proto) == "udp" {
|
||||||
|
protocol = pb.Protocol_UDP
|
||||||
|
}
|
||||||
|
|
||||||
|
var ports []*pb.PortMapping
|
||||||
|
for i := uint64(0); i <= (endPort - startPort); i++ {
|
||||||
|
containerPort = strconv.FormatUint(startPort+i, 10)
|
||||||
|
if len(hostPort) > 0 {
|
||||||
|
hostPort = strconv.FormatUint(startHostPort+i, 10)
|
||||||
|
}
|
||||||
|
// Set hostPort to a range only if there is a single container port
|
||||||
|
// and a dynamic host port.
|
||||||
|
if startPort == endPort && startHostPort != endHostPort {
|
||||||
|
hostPort = fmt.Sprintf("%s-%s", hostPort, strconv.FormatUint(endHostPort, 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrPort, err := strconv.ParseInt(containerPort, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hPort, err := strconv.ParseInt(hostPort, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
port := &pb.PortMapping{
|
||||||
|
Protocol: protocol,
|
||||||
|
ContainerPort: int32(ctrPort),
|
||||||
|
HostPort: int32(hPort),
|
||||||
|
HostIp: ip,
|
||||||
|
}
|
||||||
|
|
||||||
|
ports = append(ports, port)
|
||||||
|
}
|
||||||
|
return ports, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsePortRange parses and validates the specified string as a port-range (8000-9000)
|
||||||
|
func parsePortRange(ports string) (uint64, uint64, error) {
|
||||||
|
if ports == "" {
|
||||||
|
return 0, 0, fmt.Errorf("empty string specified for ports")
|
||||||
|
}
|
||||||
|
if !strings.Contains(ports, "-") {
|
||||||
|
start, err := strconv.ParseUint(ports, 10, 16)
|
||||||
|
end := start
|
||||||
|
return start, end, err
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(ports, "-")
|
||||||
|
start, err := strconv.ParseUint(parts[0], 10, 16)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
end, err := strconv.ParseUint(parts[1], 10, 16)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
if end < start {
|
||||||
|
return 0, 0, fmt.Errorf("Invalid range specified for the Port: %s", ports)
|
||||||
|
}
|
||||||
|
return start, end, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// splitParts separates the different parts of rawPort
|
||||||
|
func splitParts(rawport string) (string, string, string) {
|
||||||
|
parts := strings.Split(rawport, ":")
|
||||||
|
n := len(parts)
|
||||||
|
containerport := parts[n-1]
|
||||||
|
|
||||||
|
switch n {
|
||||||
|
case 1:
|
||||||
|
return "", "", containerport
|
||||||
|
case 2:
|
||||||
|
return "", parts[0], containerport
|
||||||
|
case 3:
|
||||||
|
return parts[0], parts[1], containerport
|
||||||
|
default:
|
||||||
|
return strings.Join(parts[:n-2], ":"), parts[n-2], containerport
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// splitProtoPort splits a port in the format of port/proto
|
||||||
|
func splitProtoPort(rawPort string) (string, string) {
|
||||||
|
parts := strings.Split(rawPort, "/")
|
||||||
|
l := len(parts)
|
||||||
|
if len(rawPort) == 0 || l == 0 || len(parts[0]) == 0 {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
if l == 1 {
|
||||||
|
return "tcp", rawPort
|
||||||
|
}
|
||||||
|
if len(parts[1]) == 0 {
|
||||||
|
return "tcp", parts[0]
|
||||||
|
}
|
||||||
|
return parts[1], parts[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// reads a file of line terminated key=value pairs, and overrides any keys
|
||||||
|
// present in the file with additional pairs specified in the override parameter
|
||||||
|
func readKVStrings(files []string, override []string) ([]string, error) {
|
||||||
|
envVariables := []string{}
|
||||||
|
for _, ef := range files {
|
||||||
|
parsedVars, err := parseEnvFile(ef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
envVariables = append(envVariables, parsedVars...)
|
||||||
|
}
|
||||||
|
// parse the '-e' and '--env' after, to allow override
|
||||||
|
envVariables = append(envVariables, override...)
|
||||||
|
|
||||||
|
return envVariables, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertKVStringsToMap converts ["key=value"] to {"key":"value"}
|
||||||
|
func convertKVStringsToMap(values []string) map[string]string {
|
||||||
|
result := make(map[string]string, len(values))
|
||||||
|
for _, value := range values {
|
||||||
|
kv := strings.SplitN(value, "=", 2)
|
||||||
|
if len(kv) == 1 {
|
||||||
|
result[kv[0]] = ""
|
||||||
|
} else {
|
||||||
|
result[kv[0]] = kv[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// NsIpc represents the container ipc stack.
|
||||||
|
type NsIpc string
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses its private ipc stack.
|
||||||
|
func (n NsIpc) IsPrivate() bool {
|
||||||
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's ipc stack.
|
||||||
|
func (n NsIpc) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer indicates whether the container uses a container's ipc stack.
|
||||||
|
func (n NsIpc) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the ipc stack is valid.
|
||||||
|
func (n NsIpc) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
case "container":
|
||||||
|
if len(parts) != 2 || parts[1] == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container returns the name of the container ipc stack is going to be used.
|
||||||
|
func (n NsIpc) Container() string {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// NsUser represents userns mode in the container.
|
||||||
|
type NsUser string
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's userns.
|
||||||
|
func (n NsUser) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses the a private userns.
|
||||||
|
func (n NsUser) IsPrivate() bool {
|
||||||
|
return !(n.IsHost())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the userns is valid.
|
||||||
|
func (n NsUser) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// NsPid represents the pid namespace of the container.
|
||||||
|
type NsPid string
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses its own new pid namespace.
|
||||||
|
func (n NsPid) IsPrivate() bool {
|
||||||
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's pid namespace.
|
||||||
|
func (n NsPid) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer indicates whether the container uses a container's pid namespace.
|
||||||
|
func (n NsPid) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the pid namespace is valid.
|
||||||
|
func (n NsPid) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
case "container":
|
||||||
|
if len(parts) != 2 || parts[1] == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container returns the name of the container whose pid namespace is going to be used.
|
||||||
|
func (n NsPid) Container() string {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
168
cmd/kpod/parse_test.go
Normal file
168
cmd/kpod/parse_test.go
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidateMACAddress(t *testing.T) {
|
||||||
|
addresses := []string{"01:23:45:67:89:ab",
|
||||||
|
"01:23:45:67:89:ab:cd:ef",
|
||||||
|
"01:23:45:67:89:ab:cd:ef:00:00:01:23:45:67:89:ab:cd:ef:00:00",
|
||||||
|
"01-23-45-67-89-ab",
|
||||||
|
"01-23-45-67-89-ab-cd-ef",
|
||||||
|
"01-23-45-67-89-ab-cd-ef-00-00-01-23-45-67-89-ab-cd-ef-00-00",
|
||||||
|
"0123.4567.89ab",
|
||||||
|
"0123.4567.89ab.cdef",
|
||||||
|
"0123.4567.89ab.cdef.0000.0123.4567.89ab.cdef.0000"}
|
||||||
|
|
||||||
|
for _, addr := range addresses {
|
||||||
|
_, err := validateMACAddress(addr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("invalid mac address %q", addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// test invalid mac address
|
||||||
|
invalidAddr := "567:78hy:uthg"
|
||||||
|
_, err := validateMACAddress(invalidAddr)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("should have returned an error. %q is not a valid mac address", invalidAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateLink(t *testing.T) {
|
||||||
|
validLink := "containerName:alias"
|
||||||
|
_, err := validateLink(validLink)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%q is a valid link, but error returned", validLink)
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidLinks := []string{"container:alias1:alias2", ""}
|
||||||
|
for _, link := range invalidLinks {
|
||||||
|
_, err := validateLink(link)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("should be invalid link %q, but err=nil", link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidDeviceMode(t *testing.T) {
|
||||||
|
validModes := []string{"r", "w", "m"}
|
||||||
|
for _, mode := range validModes {
|
||||||
|
valid := validDeviceMode(mode)
|
||||||
|
if !valid {
|
||||||
|
t.Fatalf("should be a valid mode %q", mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidModes := []string{"", "a", "b", "blah"}
|
||||||
|
for _, mode := range invalidModes {
|
||||||
|
valid := validDeviceMode(mode)
|
||||||
|
if valid {
|
||||||
|
t.Fatalf("should be an invalid mode %q", mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateDevice(t *testing.T) {
|
||||||
|
validDevices := []string{"host-dir:/containerPath:w",
|
||||||
|
"host:/containerPath:r",
|
||||||
|
"host:/containerPath:m",
|
||||||
|
"/containerPath",
|
||||||
|
"/containerPath:r"}
|
||||||
|
for _, dev := range validDevices {
|
||||||
|
_, err := validateDevice(dev)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
t.Fatalf("%q should be a valid device, got invalid", dev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidDevices := []string{"host:/containerPath:h",
|
||||||
|
"containerPath:r",
|
||||||
|
"/containerPath:b",
|
||||||
|
"containerPath",
|
||||||
|
""}
|
||||||
|
for _, dev := range invalidDevices {
|
||||||
|
_, err := validateDevice(dev)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
t.Fatalf("%q should be invalid device, got valid", dev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseLoggingOpts(t *testing.T) {
|
||||||
|
for _, validOpts := range []struct {
|
||||||
|
logDriver string
|
||||||
|
logDriverOpts []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
logDriver: "testDriver",
|
||||||
|
logDriverOpts: []string{"key1=value1, key2=value2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
logDriver: "testDriver",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
logDriver: "",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
_, err := parseLoggingOpts(validOpts.logDriver, validOpts.logDriverOpts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected valid logging options, got invalid %q", validOpts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, invalidOpts := range []struct {
|
||||||
|
logDriver string
|
||||||
|
logDriverOpts []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
logDriver: "none",
|
||||||
|
logDriverOpts: []string{"key1=value1, key2=value2"},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
_, err := parseLoggingOpts(invalidOpts.logDriver, invalidOpts.logDriverOpts)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected valid logging options, got invalid %q", invalidOpts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseStorageOpts(t *testing.T) {
|
||||||
|
validStorageOpts := []string{"key1=value1", "key2=value2"}
|
||||||
|
_, err := parseStorageOpts(validStorageOpts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected valid storage opts, got invalid instead %q", validStorageOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidStorageOpts := []string{"", "onlyKey", "onlyValue"}
|
||||||
|
_, err = parseStorageOpts(invalidStorageOpts)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected invalid storage opts, got valid instead %q", invalidStorageOpts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParsePortSpecs(t *testing.T) {
|
||||||
|
validPorts := []string{"123.125.234.123:8888:8888",
|
||||||
|
"123.125.234.123:8888:8888/udp",
|
||||||
|
"123.125.234.123:8888:8888/tcp",
|
||||||
|
"123.125.234.123:8888-8900:8888-8900"}
|
||||||
|
_, err := parsePortSpecs(validPorts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected valid port, got invalid instead %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidPorts := []string{"12.123.124.123.125:8000:8000",
|
||||||
|
"276.567.897.653:8000:8000",
|
||||||
|
"567:546:3455:8000:7896",
|
||||||
|
"123.124.125.134:8000:9000/blah",
|
||||||
|
""}
|
||||||
|
_, err = parsePortSpecs(invalidPorts)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected invalid port, got valid instead %v", err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue