// Copyright (c) 2016,2017 Docker, Inc. // Copyright (c) 2018 Intel Corporation // // SPDX-License-Identifier: Apache-2.0 // package main import ( "encoding/json" "fmt" "os" "strconv" "github.com/docker/go-units" "github.com/kata-containers/runtime/pkg/katautils" "github.com/kata-containers/runtime/virtcontainers/types" "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" "github.com/urfave/cli" ) func i64Ptr(i int64) *int64 { return &i } func u64Ptr(i uint64) *uint64 { return &i } func u16Ptr(i uint16) *uint16 { return &i } var updateCLICommand = cli.Command{ Name: "update", Usage: "update container resource constraints", ArgsUsage: ``, Flags: []cli.Flag{ cli.StringFlag{ Name: "resources, r", Value: "", Usage: `path to the file containing the resources to update or '-' to read from the standard input The accepted format is as follow (unchanged values can be omitted): { "memory": { "limit": 0, "reservation": 0, "swap": 0, "kernel": 0, "kernelTCP": 0 }, "cpu": { "shares": 0, "quota": 0, "period": 0, "realtimeRuntime": 0, "realtimePeriod": 0, "cpus": "", "mems": "" }, "blockIO": { "weight": 0 }, "pids": { "limit": 0 } } Note: if data is to be read from a file or the standard input, all other options are ignored. `, }, cli.IntFlag{ Name: "blkio-weight", Usage: "Specifies per cgroup weight, range is from 10 to 1000", }, cli.StringFlag{ Name: "cpu-period", Usage: "CPU CFS period to be used for hardcapping (in usecs). 0 to use system default", }, cli.StringFlag{ Name: "cpu-quota", Usage: "CPU CFS hardcap limit (in usecs). Allowed cpu time in a given period", }, cli.StringFlag{ Name: "cpu-share", Usage: "CPU shares (relative weight vs. other containers)", }, cli.StringFlag{ Name: "cpu-rt-period", Usage: "CPU realtime period to be used for hardcapping (in usecs). 0 to use system default", }, cli.StringFlag{ Name: "cpu-rt-runtime", Usage: "CPU realtime hardcap limit (in usecs). Allowed cpu time in a given period", }, cli.StringFlag{ Name: "cpuset-cpus", Usage: "CPU(s) to use", }, cli.StringFlag{ Name: "cpuset-mems", Usage: "Memory node(s) to use", }, cli.StringFlag{ Name: "kernel-memory", Usage: "Kernel memory limit (in bytes)", }, cli.StringFlag{ Name: "kernel-memory-tcp", Usage: "Kernel memory limit (in bytes) for tcp buffer", }, cli.StringFlag{ Name: "memory", Usage: "Memory limit (in bytes)", }, cli.StringFlag{ Name: "memory-reservation", Usage: "Memory reservation or soft_limit (in bytes)", }, cli.StringFlag{ Name: "memory-swap", Usage: "Total memory usage (memory + swap); set '-1' to enable unlimited swap", }, cli.IntFlag{ Name: "pids-limit", Usage: "Maximum number of pids allowed in the container", }, cli.StringFlag{ Name: "l3-cache-schema", Usage: "The string of Intel RDT/CAT L3 cache schema", }, }, Action: func(context *cli.Context) error { ctx, err := cliContextToContext(context) if err != nil { return err } span, _ := katautils.Trace(ctx, "update") defer span.Finish() if !context.Args().Present() { return fmt.Errorf("Missing container ID, should at least provide one") } containerID := context.Args().First() kataLog = kataLog.WithField("container", containerID) setExternalLoggers(ctx, kataLog) span.SetTag("container", containerID) status, sandboxID, err := getExistingContainerInfo(ctx, containerID) if err != nil { return err } containerID = status.ID kataLog = kataLog.WithFields(logrus.Fields{ "container": containerID, "sandbox": sandboxID, }) setExternalLoggers(ctx, kataLog) span.SetTag("container", containerID) span.SetTag("sandbox", sandboxID) // container MUST be running if state := status.State.State; !(state == types.StateRunning || state == types.StateReady) { return fmt.Errorf("Container %s is not running or Ready, the state is %s", containerID, state) } r := specs.LinuxResources{ Memory: &specs.LinuxMemory{ Limit: i64Ptr(0), Reservation: i64Ptr(0), Swap: i64Ptr(0), Kernel: i64Ptr(0), KernelTCP: i64Ptr(0), }, CPU: &specs.LinuxCPU{ Shares: u64Ptr(0), Quota: i64Ptr(0), Period: u64Ptr(0), RealtimeRuntime: i64Ptr(0), RealtimePeriod: u64Ptr(0), Cpus: "", Mems: "", }, BlockIO: &specs.LinuxBlockIO{ Weight: u16Ptr(0), }, Pids: &specs.LinuxPids{ Limit: 0, }, } if in := context.String("resources"); in != "" { var ( f *os.File err error ) switch in { case "-": f = os.Stdin default: f, err = os.Open(in) if err != nil { return err } } err = json.NewDecoder(f).Decode(&r) if err != nil { return err } } else { if val := context.Int("blkio-weight"); val != 0 { r.BlockIO.Weight = u16Ptr(uint16(val)) } if val := context.String("cpuset-cpus"); val != "" { r.CPU.Cpus = val } if val := context.String("cpuset-mems"); val != "" { r.CPU.Mems = val } for _, pair := range []struct { opt string dest *uint64 }{ {"cpu-period", r.CPU.Period}, {"cpu-rt-period", r.CPU.RealtimePeriod}, {"cpu-share", r.CPU.Shares}, } { if val := context.String(pair.opt); val != "" { var err error *pair.dest, err = strconv.ParseUint(val, 10, 64) if err != nil { return fmt.Errorf("invalid value for %s: %s", pair.opt, err) } } } for _, pair := range []struct { opt string dest *int64 }{ {"cpu-quota", r.CPU.Quota}, {"cpu-rt-runtime", r.CPU.RealtimeRuntime}, } { if val := context.String(pair.opt); val != "" { var err error *pair.dest, err = strconv.ParseInt(val, 10, 64) if err != nil { return fmt.Errorf("invalid value for %s: %s", pair.opt, err) } } } for _, pair := range []struct { opt string dest *int64 }{ {"memory", r.Memory.Limit}, {"memory-swap", r.Memory.Swap}, {"kernel-memory", r.Memory.Kernel}, {"kernel-memory-tcp", r.Memory.KernelTCP}, {"memory-reservation", r.Memory.Reservation}, } { if val := context.String(pair.opt); val != "" { var v int64 if val != "-1" { v, err = units.RAMInBytes(val) if err != nil { return fmt.Errorf("invalid value for %s: %s", pair.opt, err) } } else { v = -1 } *pair.dest = v } } r.Pids.Limit = int64(context.Int("pids-limit")) } return vci.UpdateContainer(ctx, sandboxID, containerID, r) }, }