diff options
Diffstat (limited to 'cmd/podman/common/specgen.go')
-rw-r--r-- | cmd/podman/common/specgen.go | 499 |
1 files changed, 291 insertions, 208 deletions
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index 87194a9fb..96cd630a3 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -1,7 +1,6 @@ package common import ( - "encoding/json" "fmt" "os" "path/filepath" @@ -23,42 +22,135 @@ import ( "github.com/pkg/errors" ) -func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) error { - var ( - err error - //namespaces map[string]string - ) +func getCPULimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) (*specs.LinuxCPU, error) { + cpu := &specs.LinuxCPU{} + hasLimits := false - // validate flags as needed - if err := c.validate(); err != nil { - return nil + 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 } - inputCommand := args[1:] - if len(c.HealthCmd) > 0 { - s.HealthConfig, err = makeHealthCheckFromCli(c.HealthCmd, c.HealthInterval, c.HealthRetries, c.HealthTimeout, c.HealthStartPeriod) + if !hasLimits { + return nil, nil + } + return cpu, nil +} + +func getIOLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) (*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 err + return nil, errors.Wrapf(err, "invalid value for blkio-weight") } + nu := uint16(u) + io.Weight = &nu + hasLimits = true } - s.IDMappings, err = util.ParseIDMapping(ns.UsernsMode(c.UserNS), c.UIDMap, c.GIDMap, c.SubUIDName, c.SubGIDName) - if err != nil { - return err + if len(c.BlkIOWeightDevice) > 0 { + if err := parseWeightDevices(c.BlkIOWeightDevice, s); 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 getPidsLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) (*specs.LinuxPids, error) { + pids := &specs.LinuxPids{} + hasLimits := false + if c.CGroupsMode == "disabled" && c.PIDsLimit > 0 { + return nil, nil + } + if c.PIDsLimit > 0 { + pids.Limit = c.PIDsLimit + hasLimits = true + } + if !hasLimits { + return nil, nil } + return pids, nil +} + +func getMemoryLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) (*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 errors.Wrapf(err, "invalid value for memory") + return nil, errors.Wrapf(err, "invalid value for memory") } - s.ResourceLimits.Memory.Limit = &ml + memory.Limit = &ml + hasLimits = true } if m := c.MemoryReservation; len(m) > 0 { mr, err := units.RAMInBytes(m) if err != nil { - return errors.Wrapf(err, "invalid value for memory") + return nil, errors.Wrapf(err, "invalid value for memory") } - s.ResourceLimits.Memory.Reservation = &mr + memory.Reservation = &mr + hasLimits = true } if m := c.MemorySwap; len(m) > 0 { var ms int64 @@ -68,99 +160,106 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string } else { ms, err = units.RAMInBytes(m) if err != nil { - return errors.Wrapf(err, "invalid value for memory") + return nil, errors.Wrapf(err, "invalid value for memory") } } - s.ResourceLimits.Memory.Swap = &ms + memory.Swap = &ms + hasLimits = true } if m := c.KernelMemory; len(m) > 0 { mk, err := units.RAMInBytes(m) if err != nil { - return errors.Wrapf(err, "invalid value for kernel-memory") + return nil, errors.Wrapf(err, "invalid value for kernel-memory") } - s.ResourceLimits.Memory.Kernel = &mk + memory.Kernel = &mk + hasLimits = true } - if b := c.BlkIOWeight; len(b) > 0 { - u, err := strconv.ParseUint(b, 10, 16) + 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 FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) error { + var ( + err error + // namespaces map[string]string + ) + + // validate flags as needed + if err := c.validate(); err != nil { + return nil + } + + 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 errors.Wrapf(err, "invalid value for blkio-weight") + return err + } + } else if c.NoHealthCheck { + s.HealthConfig = &manifest.Schema2HealthConfig{ + Test: []string{"NONE"}, } - nu := uint16(u) - s.ResourceLimits.BlockIO.Weight = &nu } - s.Terminal = c.TTY - ep, err := ExposedPorts(c.Expose, c.Net.PublishPorts, c.PublishAll, nil) + userNS := ns.UsernsMode(c.UserNS) + s.IDMappings, err = util.ParseIDMapping(userNS, c.UIDMap, c.GIDMap, c.SubUIDName, c.SubGIDName) if err != nil { return err } - s.PortMappings = ep + // If some mappings are specified, assume a private user namespace + if userNS.IsDefaultValue() && (!s.IDMappings.HostUIDMapping || !s.IDMappings.HostGIDMapping) { + s.UserNS.NSMode = specgen.Private + } + + 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.PublishImagePorts = c.PublishAll s.Pod = c.Pod - //s.CgroupNS = specgen.Namespace{ - // NSMode: , - // Value: "", - //} - - //s.UserNS = specgen.Namespace{} - - // Kernel Namespaces - // TODO Fix handling of namespace from pod - // Instead of integrating here, should be done in libpod - // However, that also involves setting up security opts - // when the pod's namespace is integrated - //namespaces = map[string]string{ - // "cgroup": c.CGroupsNS, - // "pid": c.PID, - // //"net": c.Net.Network.Value, // TODO need help here - // "ipc": c.IPC, - // "user": c.User, - // "uts": c.UTS, - //} - // - //if len(c.PID) > 0 { - // split := strings.SplitN(c.PID, ":", 2) - // // need a way to do thsi - // specgen.Namespace{ - // NSMode: split[0], - // } - // //Value: split1 if len allows - //} - // TODO this is going to have be done after things like pod creation are done because - // pod creation changes these values. - //pidMode := ns.PidMode(namespaces["pid"]) - //usernsMode := ns.UsernsMode(namespaces["user"]) - //utsMode := ns.UTSMode(namespaces["uts"]) - //cgroupMode := ns.CgroupMode(namespaces["cgroup"]) - //ipcMode := ns.IpcMode(namespaces["ipc"]) - //// Make sure if network is set to container namespace, port binding is not also being asked for - //netMode := ns.NetworkMode(namespaces["net"]) - //if netMode.IsContainer() { - // if len(portBindings) > 0 { - // return nil, errors.Errorf("cannot set port bindings on an existing container network namespace") - // } - //} - - // TODO Remove when done with namespaces for realz - // Setting a default for IPC to get this working - s.IpcNS = specgen.Namespace{ - NSMode: specgen.Private, - Value: "", - } - - // TODO this is going to have to be done the libpod/server end of things - // USER - //user := c.String("user") - //if user == "" { - // switch { - // case usernsMode.IsKeepID(): - // user = fmt.Sprintf("%d:%d", rootless.GetRootlessUID(), rootless.GetRootlessGID()) - // case data == nil: - // user = "0" - // default: - // user = data.Config.User - // } - //} + for k, v := range map[string]*specgen.Namespace{ + c.IPC: &s.IpcNS, + c.PID: &s.PidNS, + c.UTS: &s.UtsNS, + c.CGroupsNS: &s.CgroupNS, + } { + if k != "" { + *v, err = specgen.ParseNamespace(k) + 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 + } // STOP SIGNAL signalString := "TERM" @@ -190,7 +289,23 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string if c.EnvHost { env = envLib.Join(env, osEnv) + } else if c.HTTPProxy { + for _, envSpec := range []string{ + "http_proxy", + "HTTP_PROXY", + "https_proxy", + "HTTPS_PROXY", + "ftp_proxy", + "FTP_PROXY", + "no_proxy", + "NO_PROXY", + } { + if v, ok := osEnv[envSpec]; ok { + env[envSpec] = v + } + } } + // env-file overrides any previous variables for _, f := range c.EnvFile { fileEnv, err := envLib.ParseFile(f) @@ -258,6 +373,8 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string var command []string + s.Entrypoint = entrypoint + // Build the command // If we have an entry point, it goes first if len(entrypoint) > 0 { @@ -276,23 +393,27 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string } // SHM Size - shmSize, err := units.FromHumanSize(c.ShmSize) - if err != nil { - return errors.Wrapf(err, "unable to translate --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.ShmSize = &shmSize s.HostAdd = c.Net.AddHosts - s.DNSServer = c.Net.DNSServers + s.UseImageResolvConf = c.Net.UseImageResolvConf + s.DNSServers = c.Net.DNSServers s.DNSSearch = c.Net.DNSSearch - s.DNSOption = c.Net.DNSOptions - - // deferred, must be added on libpod side - //var ImageVolumes map[string]struct{} - //if data != nil && c.String("image-volume") != "ignore" { - // ImageVolumes = data.Config.Volumes - //} + s.DNSOptions = c.Net.DNSOptions + s.StaticIP = c.Net.StaticIP + s.StaticMAC = c.Net.StaticMAC + s.UseImageHosts = c.Net.NoHosts s.ImageVolumeMode = c.ImageVolume + if s.ImageVolumeMode == "bind" { + s.ImageVolumeMode = "anonymous" + } + systemd := c.SystemdD == "always" if !systemd && command != nil { x, err := strconv.ParseBool(c.SystemdD) @@ -312,14 +433,28 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string s.StopSignal = &stopSignal } } - swappiness := uint64(c.MemorySwappiness) if s.ResourceLimits == nil { s.ResourceLimits = &specs.LinuxResources{} } - if s.ResourceLimits.Memory == nil { - s.ResourceLimits.Memory = &specs.LinuxMemory{} + s.ResourceLimits.Memory, err = getMemoryLimits(s, c, args) + if err != nil { + return err + } + s.ResourceLimits.BlockIO, err = getIOLimits(s, c, args) + if err != nil { + return err + } + s.ResourceLimits.Pids, err = getPidsLimits(s, c, args) + if err != nil { + return err + } + s.ResourceLimits.CPU, err = getCPULimits(s, c, args) + if err != nil { + return err + } + if s.ResourceLimits.CPU == nil && s.ResourceLimits.Pids == nil && s.ResourceLimits.BlockIO == nil && s.ResourceLimits.Memory == nil { + s.ResourceLimits = nil } - s.ResourceLimits.Memory.Swappiness = &swappiness if s.LogConfiguration == nil { s.LogConfiguration = &specgen.LogConfig{} @@ -328,35 +463,11 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string if ld := c.LogDriver; len(ld) > 0 { s.LogConfiguration.Driver = ld } - if s.ResourceLimits.Pids == nil { - s.ResourceLimits.Pids = &specs.LinuxPids{} - } - s.ResourceLimits.Pids.Limit = c.PIDsLimit - if c.CGroups == "disabled" && c.PIDsLimit > 0 { - s.ResourceLimits.Pids.Limit = -1 - } - // TODO WTF - //cgroup := &cc.CgroupConfig{ - // Cgroups: c.String("cgroups"), - // Cgroupns: c.String("cgroupns"), - // CgroupParent: c.String("cgroup-parent"), - // CgroupMode: cgroupMode, - //} - // - //userns := &cc.UserConfig{ - // GroupAdd: c.StringSlice("group-add"), - // IDMappings: idmappings, - // UsernsMode: usernsMode, - // User: user, - //} - // - //uts := &cc.UtsConfig{ - // UtsMode: utsMode, - // NoHosts: c.Bool("no-hosts"), - // HostAdd: c.StringSlice("add-host"), - // Hostname: c.String("hostname"), - //} + 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) @@ -374,7 +485,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string // TODO // ouitside of specgen and oci though // defaults to true, check spec/storage - //s.readon = c.ReadOnlyTmpFS + // s.readon = c.ReadOnlyTmpFS // TODO convert to map? // check if key=value and convert sysmap := make(map[string]string) @@ -410,26 +521,31 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string } } - // TODO any idea why this was done - // storage.go from spec/ - // grab it - //volumes := rtc.Containers.Volumes - // TODO conflict on populate? - //if v := c.Volume; len(v)> 0 { - // s.Volumes = append(volumes, c.StringSlice("volume")...) - //} - //s.volu + s.SeccompPolicy = c.SeccompPolicy - //s.Mounts = c.Mount + // TODO: should parse out options 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, err := parseVolumes(c.Volume, c.Mount, c.TmpFS, c.ReadOnlyTmpFS && c.ReadOnly) + if err != nil { + return err + } + s.Mounts = mounts + s.Volumes = volumes + // TODO any idea why this was done - //devices := rtc.Containers.Devices + // devices := rtc.Containers.Devices // TODO conflict on populate? // - //if c.Changed("device") { + // if c.Changed("device") { // devices = append(devices, c.StringSlice("device")...) - //} + // } + + for _, dev := range c.Devices { + s.Devices = append(s.Devices, specs.LinuxDevice{Path: dev}) + } // TODO things i cannot find in spec // we dont think these are in the spec @@ -437,33 +553,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string // initpath s.Stdin = c.Interactive // quiet - //DeviceCgroupRules: c.StringSlice("device-cgroup-rule"), - - if bps := c.DeviceReadBPs; len(bps) > 0 { - if s.ThrottleReadBpsDevice, err = parseThrottleBPSDevices(bps); err != nil { - return err - } - } - - if bps := c.DeviceWriteBPs; len(bps) > 0 { - if s.ThrottleWriteBpsDevice, err = parseThrottleBPSDevices(bps); err != nil { - return err - } - } - - if iops := c.DeviceReadIOPs; len(iops) > 0 { - if s.ThrottleReadIOPSDevice, err = parseThrottleIOPsDevices(iops); err != nil { - return err - } - } - - if iops := c.DeviceWriteIOPs; len(iops) > 0 { - if s.ThrottleWriteIOPSDevice, err = parseThrottleIOPsDevices(iops); err != nil { - return err - } - } - - s.ResourceLimits.Memory.DisableOOMKiller = &c.OOMKillDisable + // DeviceCgroupRules: c.StringSlice("device-cgroup-rule"), // Rlimits/Ulimits for _, u := range c.Ulimit { @@ -483,10 +573,10 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string s.Rlimits = append(s.Rlimits, rl) } - //Tmpfs: c.StringArray("tmpfs"), + // Tmpfs: c.StringArray("tmpfs"), // TODO how to handle this? - //Syslog: c.Bool("syslog"), + // Syslog: c.Bool("syslog"), logOpts := make(map[string]string) for _, o := range c.LogOptions { @@ -494,37 +584,25 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string if len(split) < 2 { return errors.Errorf("invalid log option %q", o) } - logOpts[split[0]] = split[1] + switch { + case split[0] == "driver": + s.LogConfiguration.Driver = split[1] + case split[0] == "path": + s.LogConfiguration.Path = split[1] + default: + logOpts[split[0]] = split[1] + } } s.LogConfiguration.Options = logOpts s.Name = c.Name - if err := parseWeightDevices(c.BlkIOWeightDevice, s); err != nil { - return err - } - - if s.ResourceLimits.CPU == nil { - s.ResourceLimits.CPU = &specs.LinuxCPU{} - } - s.ResourceLimits.CPU.Shares = &c.CPUShares - s.ResourceLimits.CPU.Period = &c.CPUPeriod - - // TODO research these - //s.ResourceLimits.CPU.Cpus = c.CPUS - //s.ResourceLimits.CPU.Cpus = c.CPUSetCPUs - - //s.ResourceLimits.CPU. = c.CPUSetCPUs - s.ResourceLimits.CPU.Mems = c.CPUSetMems - s.ResourceLimits.CPU.Quota = &c.CPUQuota - s.ResourceLimits.CPU.RealtimePeriod = &c.CPURTPeriod - s.ResourceLimits.CPU.RealtimeRuntime = &c.CPURTRuntime s.OOMScoreAdj = &c.OOMScoreAdj s.RestartPolicy = c.Restart s.Remove = c.Rm s.StopTimeout = &c.StopTimeout // TODO where should we do this? - //func verifyContainerResources(config *cc.CreateConfig, update bool) ([]string, error) { + // func verifyContainerResources(config *cc.CreateConfig, update bool) ([]string, error) { return nil } @@ -536,10 +614,15 @@ func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, start // first try to parse option value as JSON array of strings... cmd := []string{} - err := json.Unmarshal([]byte(inCmd), &cmd) - if err != nil { - // ...otherwise pass it to "/bin/sh -c" inside the container - cmd = []string{"CMD-SHELL", inCmd} + + if inCmd == "none" { + cmd = []string{"NONE"} + } else { + err := json.Unmarshal([]byte(inCmd), &cmd) + if err != nil { + // ...otherwise pass it to "/bin/sh -c" inside the container + cmd = []string{"CMD-SHELL", inCmd} + } } hc := manifest.Schema2HealthConfig{ Test: cmd, |