diff options
Diffstat (limited to 'cmd/podman/common/specgen.go')
-rw-r--r-- | cmd/podman/common/specgen.go | 972 |
1 files changed, 0 insertions, 972 deletions
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go deleted file mode 100644 index 59d32f568..000000000 --- a/cmd/podman/common/specgen.go +++ /dev/null @@ -1,972 +0,0 @@ -package common - -import ( - "fmt" - "os" - "strconv" - "strings" - "time" - - "github.com/containers/image/v5/manifest" - "github.com/containers/podman/v3/cmd/podman/parse" - "github.com/containers/podman/v3/libpod/define" - ann "github.com/containers/podman/v3/pkg/annotations" - envLib "github.com/containers/podman/v3/pkg/env" - ns "github.com/containers/podman/v3/pkg/namespaces" - "github.com/containers/podman/v3/pkg/specgen" - systemdDefine "github.com/containers/podman/v3/pkg/systemd/define" - "github.com/containers/podman/v3/pkg/util" - "github.com/docker/go-units" - "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" -) - -func getCPULimits(c *ContainerCLIOpts) *specs.LinuxCPU { - cpu := &specs.LinuxCPU{} - hasLimits := false - - if c.CPUS > 0 { - period, quota := util.CoresToPeriodAndQuota(c.CPUS) - - cpu.Period = &period - cpu.Quota = "a - hasLimits = true - } - if c.CPUShares > 0 { - cpu.Shares = &c.CPUShares - hasLimits = true - } - if c.CPUPeriod > 0 { - cpu.Period = &c.CPUPeriod - hasLimits = true - } - if c.CPUSetCPUs != "" { - cpu.Cpus = c.CPUSetCPUs - hasLimits = true - } - if c.CPUSetMems != "" { - cpu.Mems = c.CPUSetMems - hasLimits = true - } - if c.CPUQuota > 0 { - cpu.Quota = &c.CPUQuota - hasLimits = true - } - if c.CPURTPeriod > 0 { - cpu.RealtimePeriod = &c.CPURTPeriod - hasLimits = true - } - if c.CPURTRuntime > 0 { - cpu.RealtimeRuntime = &c.CPURTRuntime - hasLimits = true - } - - if !hasLimits { - return nil - } - return cpu -} - -func getIOLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.LinuxBlockIO, error) { - var err error - io := &specs.LinuxBlockIO{} - hasLimits := false - if b := c.BlkIOWeight; len(b) > 0 { - u, err := strconv.ParseUint(b, 10, 16) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for blkio-weight") - } - nu := uint16(u) - io.Weight = &nu - hasLimits = true - } - - if len(c.BlkIOWeightDevice) > 0 { - if err := parseWeightDevices(s, c.BlkIOWeightDevice); err != nil { - return nil, err - } - hasLimits = true - } - - if bps := c.DeviceReadBPs; len(bps) > 0 { - if s.ThrottleReadBpsDevice, err = parseThrottleBPSDevices(bps); err != nil { - return nil, err - } - hasLimits = true - } - - if bps := c.DeviceWriteBPs; len(bps) > 0 { - if s.ThrottleWriteBpsDevice, err = parseThrottleBPSDevices(bps); err != nil { - return nil, err - } - hasLimits = true - } - - if iops := c.DeviceReadIOPs; len(iops) > 0 { - if s.ThrottleReadIOPSDevice, err = parseThrottleIOPsDevices(iops); err != nil { - return nil, err - } - hasLimits = true - } - - if iops := c.DeviceWriteIOPs; len(iops) > 0 { - if s.ThrottleWriteIOPSDevice, err = parseThrottleIOPsDevices(iops); err != nil { - return nil, err - } - hasLimits = true - } - - if !hasLimits { - return nil, nil - } - return io, nil -} - -func getMemoryLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.LinuxMemory, error) { - var err error - memory := &specs.LinuxMemory{} - hasLimits := false - if m := c.Memory; len(m) > 0 { - ml, err := units.RAMInBytes(m) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory") - } - memory.Limit = &ml - if c.MemorySwap == "" { - limit := 2 * ml - memory.Swap = &(limit) - } - hasLimits = true - } - if m := c.MemoryReservation; len(m) > 0 { - mr, err := units.RAMInBytes(m) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory") - } - memory.Reservation = &mr - hasLimits = true - } - if m := c.MemorySwap; len(m) > 0 { - var ms int64 - // only set memory swap if it was set - // -1 indicates unlimited - if m != "-1" { - ms, err = units.RAMInBytes(m) - memory.Swap = &ms - if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory") - } - hasLimits = true - } - } - if m := c.KernelMemory; len(m) > 0 { - mk, err := units.RAMInBytes(m) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for kernel-memory") - } - memory.Kernel = &mk - hasLimits = true - } - if c.MemorySwappiness >= 0 { - swappiness := uint64(c.MemorySwappiness) - memory.Swappiness = &swappiness - hasLimits = true - } - if c.OOMKillDisable { - memory.DisableOOMKiller = &c.OOMKillDisable - hasLimits = true - } - if !hasLimits { - return nil, nil - } - return memory, nil -} - -func setNamespaces(s *specgen.SpecGenerator, c *ContainerCLIOpts) error { - var err error - - if c.PID != "" { - s.PidNS, err = specgen.ParseNamespace(c.PID) - if err != nil { - return err - } - } - if c.IPC != "" { - s.IpcNS, err = specgen.ParseNamespace(c.IPC) - if err != nil { - return err - } - } - if c.UTS != "" { - s.UtsNS, err = specgen.ParseNamespace(c.UTS) - if err != nil { - return err - } - } - if c.CgroupNS != "" { - s.CgroupNS, err = specgen.ParseNamespace(c.CgroupNS) - if err != nil { - return err - } - } - // userns must be treated differently - if c.UserNS != "" { - s.UserNS, err = specgen.ParseUserNamespace(c.UserNS) - if err != nil { - return err - } - } - if c.Net != nil { - s.NetNS = c.Net.Network - } - return nil -} - -func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) error { - var ( - err error - ) - - // validate flags as needed - if err := c.validate(); err != nil { - return err - } - - s.User = c.User - inputCommand := args[1:] - if len(c.HealthCmd) > 0 { - if c.NoHealthCheck { - return errors.New("Cannot specify both --no-healthcheck and --health-cmd") - } - s.HealthConfig, err = makeHealthCheckFromCli(c.HealthCmd, c.HealthInterval, c.HealthRetries, c.HealthTimeout, c.HealthStartPeriod) - if err != nil { - return err - } - } else if c.NoHealthCheck { - s.HealthConfig = &manifest.Schema2HealthConfig{ - Test: []string{"NONE"}, - } - } - - userNS := ns.UsernsMode(c.UserNS) - s.IDMappings, err = util.ParseIDMapping(userNS, c.UIDMap, c.GIDMap, c.SubUIDName, c.SubGIDName) - if err != nil { - return err - } - // If some mappings are specified, assume a private user namespace - if userNS.IsDefaultValue() && (!s.IDMappings.HostUIDMapping || !s.IDMappings.HostGIDMapping) { - s.UserNS.NSMode = specgen.Private - } else { - s.UserNS.NSMode = specgen.NamespaceMode(userNS) - } - - s.Terminal = c.TTY - - if err := verifyExpose(c.Expose); err != nil { - return err - } - // We are not handling the Expose flag yet. - // s.PortsExpose = c.Expose - s.PortMappings = c.Net.PublishPorts - s.PublishExposedPorts = c.PublishAll - s.Pod = c.Pod - - if len(c.PodIDFile) > 0 { - if len(s.Pod) > 0 { - return errors.New("Cannot specify both --pod and --pod-id-file") - } - podID, err := ReadPodIDFile(c.PodIDFile) - if err != nil { - return err - } - s.Pod = podID - } - - expose, err := createExpose(c.Expose) - if err != nil { - return err - } - s.Expose = expose - - if err := setNamespaces(s, c); err != nil { - return err - } - - if sig := c.StopSignal; len(sig) > 0 { - stopSignal, err := util.ParseSignal(sig) - if err != nil { - return err - } - s.StopSignal = &stopSignal - } - - // ENVIRONMENT VARIABLES - // - // Precedence order (higher index wins): - // 1) containers.conf (EnvHost, EnvHTTP, Env) 2) image data, 3 User EnvHost/EnvHTTP, 4) env-file, 5) env - // containers.conf handled and image data handled on the server side - // user specified EnvHost and EnvHTTP handled on Server Side relative to Server - // env-file and env handled on client side - var env map[string]string - - // First transform the os env into a map. We need it for the labels later in - // any case. - osEnv, err := envLib.ParseSlice(os.Environ()) - if err != nil { - return errors.Wrap(err, "error parsing host environment variables") - } - - s.EnvHost = c.EnvHost - s.HTTPProxy = c.HTTPProxy - - // env-file overrides any previous variables - for _, f := range c.EnvFile { - fileEnv, err := envLib.ParseFile(f) - if err != nil { - return err - } - // File env is overridden by env. - env = envLib.Join(env, fileEnv) - } - - parsedEnv, err := envLib.ParseSlice(c.Env) - if err != nil { - return err - } - - s.Env = envLib.Join(env, parsedEnv) - - // LABEL VARIABLES - labels, err := parse.GetAllLabels(c.LabelFile, c.Label) - if err != nil { - return errors.Wrapf(err, "unable to process labels") - } - - if systemdUnit, exists := osEnv[systemdDefine.EnvVariable]; exists { - labels[systemdDefine.EnvVariable] = systemdUnit - } - - s.Labels = labels - - // ANNOTATIONS - annotations := make(map[string]string) - - // First, add our default annotations - annotations[ann.TTY] = "false" - if c.TTY { - annotations[ann.TTY] = "true" - } - - // Last, add user annotations - for _, annotation := range c.Annotation { - splitAnnotation := strings.SplitN(annotation, "=", 2) - if len(splitAnnotation) < 2 { - return errors.Errorf("Annotations must be formatted KEY=VALUE") - } - annotations[splitAnnotation[0]] = splitAnnotation[1] - } - s.Annotations = annotations - - s.WorkDir = c.Workdir - if c.Entrypoint != nil { - entrypoint := []string{} - if ep := *c.Entrypoint; len(ep) > 0 { - // Check if entrypoint specified is json - if err := json.Unmarshal([]byte(*c.Entrypoint), &entrypoint); err != nil { - entrypoint = append(entrypoint, ep) - } - } - s.Entrypoint = entrypoint - } - - // Include the command used to create the container. - s.ContainerCreateCommand = os.Args - - if len(inputCommand) > 0 { - s.Command = inputCommand - } - - // SHM Size - if c.ShmSize != "" { - shmSize, err := units.FromHumanSize(c.ShmSize) - if err != nil { - return errors.Wrapf(err, "unable to translate --shm-size") - } - s.ShmSize = &shmSize - } - s.CNINetworks = c.Net.CNINetworks - - // Network aliases - if len(c.Net.Aliases) > 0 { - // build a map of aliases where key=cniName - aliases := make(map[string][]string, len(s.CNINetworks)) - for _, cniNetwork := range s.CNINetworks { - aliases[cniNetwork] = c.Net.Aliases - } - s.Aliases = aliases - } - - s.HostAdd = c.Net.AddHosts - s.UseImageResolvConf = c.Net.UseImageResolvConf - s.DNSServers = c.Net.DNSServers - s.DNSSearch = c.Net.DNSSearch - s.DNSOptions = c.Net.DNSOptions - s.StaticIP = c.Net.StaticIP - s.StaticMAC = c.Net.StaticMAC - s.NetworkOptions = c.Net.NetworkOptions - s.UseImageHosts = c.Net.NoHosts - - s.ImageVolumeMode = c.ImageVolume - if s.ImageVolumeMode == "bind" { - s.ImageVolumeMode = "anonymous" - } - - s.Systemd = c.Systemd - s.SdNotifyMode = c.SdNotifyMode - if s.ResourceLimits == nil { - s.ResourceLimits = &specs.LinuxResources{} - } - s.ResourceLimits.Memory, err = getMemoryLimits(s, c) - if err != nil { - return err - } - s.ResourceLimits.BlockIO, err = getIOLimits(s, c) - if err != nil { - return err - } - if c.PIDsLimit != nil { - pids := specs.LinuxPids{ - Limit: *c.PIDsLimit, - } - - s.ResourceLimits.Pids = &pids - } - s.ResourceLimits.CPU = getCPULimits(c) - - unifieds := make(map[string]string) - for _, unified := range c.CgroupConf { - splitUnified := strings.SplitN(unified, "=", 2) - if len(splitUnified) < 2 { - return errors.Errorf("--cgroup-conf must be formatted KEY=VALUE") - } - unifieds[splitUnified[0]] = splitUnified[1] - } - if len(unifieds) > 0 { - s.ResourceLimits.Unified = unifieds - } - - if s.ResourceLimits.CPU == nil && s.ResourceLimits.Pids == nil && s.ResourceLimits.BlockIO == nil && s.ResourceLimits.Memory == nil && s.ResourceLimits.Unified == nil { - s.ResourceLimits = nil - } - - if s.LogConfiguration == nil { - s.LogConfiguration = &specgen.LogConfig{} - } - - if ld := c.LogDriver; len(ld) > 0 { - s.LogConfiguration.Driver = ld - } - s.CgroupParent = c.CGroupParent - s.CgroupsMode = c.CGroupsMode - s.Groups = c.GroupAdd - - s.Hostname = c.Hostname - sysctl := map[string]string{} - if ctl := c.Sysctl; len(ctl) > 0 { - sysctl, err = util.ValidateSysctls(ctl) - if err != nil { - return err - } - } - s.Sysctl = sysctl - - s.CapAdd = c.CapAdd - s.CapDrop = c.CapDrop - s.Privileged = c.Privileged - s.ReadOnlyFilesystem = c.ReadOnly - s.ConmonPidFile = c.ConmonPIDFile - - s.DependencyContainers = c.Requires - - // TODO - // outside of specgen and oci though - // defaults to true, check spec/storage - // s.readonly = c.ReadOnlyTmpFS - // TODO convert to map? - // check if key=value and convert - sysmap := make(map[string]string) - for _, ctl := range c.Sysctl { - splitCtl := strings.SplitN(ctl, "=", 2) - if len(splitCtl) < 2 { - return errors.Errorf("invalid sysctl value %q", ctl) - } - sysmap[splitCtl[0]] = splitCtl[1] - } - s.Sysctl = sysmap - - if c.CIDFile != "" { - s.Annotations[define.InspectAnnotationCIDFile] = c.CIDFile - } - - for _, opt := range c.SecurityOpt { - if opt == "no-new-privileges" { - s.ContainerSecurityConfig.NoNewPrivileges = true - } else { - con := strings.SplitN(opt, "=", 2) - if len(con) != 2 { - return fmt.Errorf("invalid --security-opt 1: %q", opt) - } - switch con[0] { - case "apparmor": - s.ContainerSecurityConfig.ApparmorProfile = con[1] - s.Annotations[define.InspectAnnotationApparmor] = con[1] - case "label": - // TODO selinux opts and label opts are the same thing - s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1]) - s.Annotations[define.InspectAnnotationLabel] = strings.Join(s.ContainerSecurityConfig.SelinuxOpts, ",label=") - case "mask": - s.ContainerSecurityConfig.Mask = append(s.ContainerSecurityConfig.Mask, strings.Split(con[1], ":")...) - case "proc-opts": - s.ProcOpts = strings.Split(con[1], ",") - case "seccomp": - s.SeccompProfilePath = con[1] - s.Annotations[define.InspectAnnotationSeccomp] = con[1] - // this option is for docker compatibility, it is the same as unmask=ALL - case "systempaths": - if con[1] == "unconfined" { - s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, []string{"ALL"}...) - } else { - return fmt.Errorf("invalid systempaths option %q, only `unconfined` is supported", con[1]) - } - case "unmask": - s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, con[1:]...) - default: - return fmt.Errorf("invalid --security-opt 2: %q", opt) - } - } - } - - s.SeccompPolicy = c.SeccompPolicy - - s.VolumesFrom = c.VolumesFrom - - // Only add read-only tmpfs mounts in case that we are read-only and the - // read-only tmpfs flag has been set. - mounts, volumes, overlayVolumes, imageVolumes, err := parseVolumes(c.Volume, c.Mount, c.TmpFS, c.ReadOnlyTmpFS && c.ReadOnly) - if err != nil { - return err - } - s.Mounts = mounts - s.Volumes = volumes - s.OverlayVolumes = overlayVolumes - s.ImageVolumes = imageVolumes - - for _, dev := range c.Devices { - s.Devices = append(s.Devices, specs.LinuxDevice{Path: dev}) - } - - for _, rule := range c.DeviceCGroupRule { - dev, err := parseLinuxResourcesDeviceAccess(rule) - if err != nil { - return err - } - s.DeviceCGroupRule = append(s.DeviceCGroupRule, dev) - } - - s.Init = c.Init - s.InitPath = c.InitPath - s.Stdin = c.Interactive - // quiet - // DeviceCgroupRules: c.StringSlice("device-cgroup-rule"), - - // Rlimits/Ulimits - for _, u := range c.Ulimit { - if u == "host" { - s.Rlimits = nil - break - } - ul, err := units.ParseUlimit(u) - if err != nil { - return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u) - } - rl := specs.POSIXRlimit{ - Type: ul.Name, - Hard: uint64(ul.Hard), - Soft: uint64(ul.Soft), - } - s.Rlimits = append(s.Rlimits, rl) - } - - logOpts := make(map[string]string) - for _, o := range c.LogOptions { - split := strings.SplitN(o, "=", 2) - if len(split) < 2 { - return errors.Errorf("invalid log option %q", o) - } - switch strings.ToLower(split[0]) { - case "driver": - s.LogConfiguration.Driver = split[1] - case "path": - s.LogConfiguration.Path = split[1] - case "max-size": - logSize, err := units.FromHumanSize(split[1]) - if err != nil { - return err - } - s.LogConfiguration.Size = logSize - default: - logOpts[split[0]] = split[1] - } - } - s.LogConfiguration.Options = logOpts - s.Name = c.Name - s.PreserveFDs = c.PreserveFDs - - s.OOMScoreAdj = &c.OOMScoreAdj - if c.Restart != "" { - splitRestart := strings.Split(c.Restart, ":") - switch len(splitRestart) { - case 1: - // No retries specified - case 2: - if strings.ToLower(splitRestart[0]) != "on-failure" { - return errors.Errorf("restart policy retries can only be specified with on-failure restart policy") - } - retries, err := strconv.Atoi(splitRestart[1]) - if err != nil { - return errors.Wrapf(err, "error parsing restart policy retry count") - } - if retries < 0 { - return errors.Errorf("must specify restart policy retry count as a number greater than 0") - } - var retriesUint = uint(retries) - s.RestartRetries = &retriesUint - default: - return errors.Errorf("invalid restart policy: may specify retries at most once") - } - s.RestartPolicy = splitRestart[0] - } - - s.Secrets, s.EnvSecrets, err = parseSecrets(c.Secrets) - if err != nil { - return err - } - - if c.Personality != "" { - s.Personality = &specs.LinuxPersonality{} - s.Personality.Domain = specs.LinuxPersonalityDomain(c.Personality) - } - - s.Remove = c.Rm - s.StopTimeout = &c.StopTimeout - s.Timeout = c.Timeout - s.Timezone = c.Timezone - s.Umask = c.Umask - s.PidFile = c.PidFile - s.Volatile = c.Rm - - // Initcontainers - s.InitContainerType = c.InitContainerType - return nil -} - -func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, startPeriod string) (*manifest.Schema2HealthConfig, error) { - cmdArr := []string{} - isArr := true - err := json.Unmarshal([]byte(inCmd), &cmdArr) // array unmarshalling - if err != nil { - cmdArr = strings.SplitN(inCmd, " ", 2) // default for compat - isArr = false - } - // Every healthcheck requires a command - if len(cmdArr) == 0 { - return nil, errors.New("Must define a healthcheck command for all healthchecks") - } - concat := "" - if cmdArr[0] == "CMD" || cmdArr[0] == "none" { // this is for compat, we are already split properly for most compat cases - cmdArr = strings.Fields(inCmd) - } else if cmdArr[0] != "CMD-SHELL" { // this is for podman side of things, won't contain the keywords - if isArr && len(cmdArr) > 1 { // an array of consecutive commands - cmdArr = append([]string{"CMD"}, cmdArr...) - } else { // one singular command - if len(cmdArr) == 1 { - concat = cmdArr[0] - } else { - concat = strings.Join(cmdArr[0:], " ") - } - cmdArr = append([]string{"CMD-SHELL"}, concat) - } - } - - if cmdArr[0] == "none" { // if specified to remove healtcheck - cmdArr = []string{"NONE"} - } - - // healthcheck is by default an array, so we simply pass the user input - hc := manifest.Schema2HealthConfig{ - Test: cmdArr, - } - - if interval == "disable" { - interval = "0" - } - intervalDuration, err := time.ParseDuration(interval) - if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-interval") - } - - hc.Interval = intervalDuration - - if retries < 1 { - return nil, errors.New("healthcheck-retries must be greater than 0") - } - hc.Retries = int(retries) - timeoutDuration, err := time.ParseDuration(timeout) - if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-timeout") - } - if timeoutDuration < time.Duration(1) { - return nil, errors.New("healthcheck-timeout must be at least 1 second") - } - hc.Timeout = timeoutDuration - - startPeriodDuration, err := time.ParseDuration(startPeriod) - if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-start-period") - } - if startPeriodDuration < time.Duration(0) { - return nil, errors.New("healthcheck-start-period must be 0 seconds or greater") - } - hc.StartPeriod = startPeriodDuration - - return &hc, nil -} - -func parseWeightDevices(s *specgen.SpecGenerator, weightDevs []string) error { - for _, val := range weightDevs { - split := strings.SplitN(val, ":", 2) - if len(split) != 2 { - return fmt.Errorf("bad format: %s", val) - } - if !strings.HasPrefix(split[0], "/dev/") { - return fmt.Errorf("bad format for device path: %s", val) - } - weight, err := strconv.ParseUint(split[1], 10, 0) - if err != nil { - return fmt.Errorf("invalid weight for device: %s", val) - } - if weight > 0 && (weight < 10 || weight > 1000) { - return fmt.Errorf("invalid weight for device: %s", val) - } - w := uint16(weight) - s.WeightDevice[split[0]] = specs.LinuxWeightDevice{ - Weight: &w, - LeafWeight: nil, - } - } - return nil -} - -func parseThrottleBPSDevices(bpsDevices []string) (map[string]specs.LinuxThrottleDevice, error) { - td := make(map[string]specs.LinuxThrottleDevice) - for _, val := range bpsDevices { - 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) - } - td[split[0]] = specs.LinuxThrottleDevice{Rate: uint64(rate)} - } - return td, nil -} - -func parseThrottleIOPsDevices(iopsDevices []string) (map[string]specs.LinuxThrottleDevice, error) { - td := make(map[string]specs.LinuxThrottleDevice) - for _, val := range iopsDevices { - 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) - } - td[split[0]] = specs.LinuxThrottleDevice{Rate: rate} - } - return td, nil -} - -func parseSecrets(secrets []string) ([]specgen.Secret, map[string]string, error) { - secretParseError := errors.New("error parsing secret") - var mount []specgen.Secret - envs := make(map[string]string) - for _, val := range secrets { - // mount only tells if user has set an option that can only be used with mount secret type - mountOnly := false - source := "" - secretType := "" - target := "" - var uid, gid uint32 - // default mode 444 octal = 292 decimal - var mode uint32 = 292 - split := strings.Split(val, ",") - - // --secret mysecret - if len(split) == 1 { - mountSecret := specgen.Secret{ - Source: val, - UID: uid, - GID: gid, - Mode: mode, - } - mount = append(mount, mountSecret) - continue - } - // --secret mysecret,opt=opt - if !strings.Contains(split[0], "=") { - source = split[0] - split = split[1:] - } - - for _, val := range split { - kv := strings.SplitN(val, "=", 2) - if len(kv) < 2 { - return nil, nil, errors.Wrapf(secretParseError, "option %s must be in form option=value", val) - } - switch kv[0] { - case "source": - source = kv[1] - case "type": - if secretType != "" { - return nil, nil, errors.Wrap(secretParseError, "cannot set more tha one secret type") - } - if kv[1] != "mount" && kv[1] != "env" { - return nil, nil, errors.Wrapf(secretParseError, "type %s is invalid", kv[1]) - } - secretType = kv[1] - case "target": - target = kv[1] - case "mode": - mountOnly = true - mode64, err := strconv.ParseUint(kv[1], 8, 32) - if err != nil { - return nil, nil, errors.Wrapf(secretParseError, "mode %s invalid", kv[1]) - } - mode = uint32(mode64) - case "uid", "UID": - mountOnly = true - uid64, err := strconv.ParseUint(kv[1], 10, 32) - if err != nil { - return nil, nil, errors.Wrapf(secretParseError, "UID %s invalid", kv[1]) - } - uid = uint32(uid64) - case "gid", "GID": - mountOnly = true - gid64, err := strconv.ParseUint(kv[1], 10, 32) - if err != nil { - return nil, nil, errors.Wrapf(secretParseError, "GID %s invalid", kv[1]) - } - gid = uint32(gid64) - - default: - return nil, nil, errors.Wrapf(secretParseError, "option %s invalid", val) - } - } - - if secretType == "" { - secretType = "mount" - } - if source == "" { - return nil, nil, errors.Wrapf(secretParseError, "no source found %s", val) - } - if secretType == "mount" { - if target != "" { - return nil, nil, errors.Wrapf(secretParseError, "target option is invalid for mounted secrets") - } - mountSecret := specgen.Secret{ - Source: source, - UID: uid, - GID: gid, - Mode: mode, - } - mount = append(mount, mountSecret) - } - if secretType == "env" { - if mountOnly { - return nil, nil, errors.Wrap(secretParseError, "UID, GID, Mode options cannot be set with secret type env") - } - if target == "" { - target = source - } - envs[target] = source - } - } - return mount, envs, nil -} - -var cgroupDeviceType = map[string]bool{ - "a": true, // all - "b": true, // block device - "c": true, // character device -} - -var cgroupDeviceAccess = map[string]bool{ - "r": true, //read - "w": true, //write - "m": true, //mknod -} - -// parseLinuxResourcesDeviceAccess parses the raw string passed with the --device-access-add flag -func parseLinuxResourcesDeviceAccess(device string) (specs.LinuxDeviceCgroup, error) { - var devType, access string - var major, minor *int64 - - value := strings.Split(device, " ") - if len(value) != 3 { - return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device cgroup rule requires type, major:Minor, and access rules: %q", device) - } - - devType = value[0] - if !cgroupDeviceType[devType] { - return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device type in device-access-add: %s", devType) - } - - number := strings.SplitN(value[1], ":", 2) - i, err := strconv.ParseInt(number[0], 10, 64) - if err != nil { - return specs.LinuxDeviceCgroup{}, err - } - major = &i - if len(number) == 2 && number[1] != "*" { - i, err := strconv.ParseInt(number[1], 10, 64) - if err != nil { - return specs.LinuxDeviceCgroup{}, err - } - minor = &i - } - access = value[2] - for _, c := range strings.Split(access, "") { - if !cgroupDeviceAccess[c] { - return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device access in device-access-add: %s", c) - } - } - return specs.LinuxDeviceCgroup{ - Allow: true, - Type: devType, - Major: major, - Minor: minor, - Access: access, - }, nil -} |