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