| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package compute
- import (
- "os"
- "strconv"
- "strings"
- "time"
- "yunion.io/x/jsonutils"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/onecloud/pkg/apis"
- computeapi "yunion.io/x/onecloud/pkg/apis/compute"
- "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
- "yunion.io/x/onecloud/pkg/mcclient/options"
- )
- type ContainerListOptions struct {
- options.BaseListOptions
- GuestId string `json:"guest_id" help:"guest(pod) id or name"`
- HostId string `json:"host_id" help:"host id or name"`
- }
- func (o *ContainerListOptions) Params() (jsonutils.JSONObject, error) {
- return options.ListStructToParams(o)
- }
- type ContainerShowOptions struct {
- ServerIdOptions
- }
- type ContainerDeleteOptions struct {
- ServerIdsOptions
- }
- type ContainerCreateCommonOptions struct {
- IMAGE string `help:"Image of container" json:"image"`
- ImageCredentialId string `help:"Image credential id" json:"image_credential_id"`
- Command []string `help:"Command to execute (i.e., entrypoint for docker)" json:"command"`
- Args []string `help:"Args for the Command (i.e. command for docker)" json:"args"`
- WorkingDir string `help:"Current working directory of the command" json:"working_dir"`
- Env []string `help:"List of environment variable to set in the container and the format is: <key>=<value>"`
- RootFs string `help:"Root filesystem of the container, e.g.: disk_index=<disk_number>,disk_id=<disk_id>"`
- VolumeMount []string `help:"Volume mount of the container and the format is: name=<val>,mount_path=<container_path>,readonly=<true_or_false>,case_insensitive_paths=p1,p2,disk_index=<disk_number>,disk_id=<disk_id>"`
- Device []string `help:"Host device: <host_path>:<container_path>:<permissions>, e.g.: /dev/snd:/dev/snd:rwm"`
- Privileged bool `help:"Privileged mode"`
- Caps string `help:"Container capabilities, e.g.: SETPCAP,AUDIT_WRITE,SYS_CHROOT,CHOWN,DAC_OVERRIDE,FOWNER,SETGID,SETUID,SYSLOG,SYS_ADMIN,WAKE_ALARM,SYS_PTRACE,BLOCK_SUSPEND,MKNOD,KILL,SYS_RESOURCE,NET_RAW,NET_ADMIN,NET_BIND_SERVICE,SYS_NICE"`
- DropCaps string `help:"Container dropped capabilities, split by ','"`
- EnableLxcfs bool `help:"Enable lxcfs"`
- PostStartExec string `help:"Post started execution command"`
- CgroupDeviceAllow []string `help:"Cgroup devices.allow, e.g.: 'c 13:* rwm'"`
- SimulateCpu bool `help:"Simulating /sys/devices/system/cpu files"`
- ShmSizeMb int `help:"Shm size MB"`
- Uid int64 `help:"UID of container" default:"0"`
- Gid int64 `help:"GID of container" default:"0"`
- DisableNoNewPrivs bool `help:"Disable no_new_privs flag of the container"`
- Apparmor string `help:"Apparmor profile for container"`
- }
- func (o ContainerCreateCommonOptions) getCreateSpec() (*computeapi.ContainerSpec, error) {
- req := &computeapi.ContainerSpec{
- ContainerSpec: apis.ContainerSpec{
- Image: o.IMAGE,
- ImageCredentialId: o.ImageCredentialId,
- Command: o.Command,
- Args: o.Args,
- WorkingDir: o.WorkingDir,
- EnableLxcfs: o.EnableLxcfs,
- Privileged: o.Privileged,
- Capabilities: &apis.ContainerCapability{},
- CgroupDevicesAllow: o.CgroupDeviceAllow,
- SimulateCpu: o.SimulateCpu,
- DisableNoNewPrivs: o.DisableNoNewPrivs,
- SecurityContext: &apis.ContainerSecurityContext{
- RunAsUser: nil,
- RunAsGroup: nil,
- },
- },
- }
- if o.ShmSizeMb > 0 {
- req.ContainerSpec.ShmSizeMB = o.ShmSizeMb
- }
- if o.Uid > 0 {
- req.ContainerSpec.SecurityContext.RunAsUser = &o.Uid
- }
- if o.Gid > 0 {
- req.ContainerSpec.SecurityContext.RunAsGroup = &o.Gid
- }
- if o.Apparmor != "" {
- req.ContainerSpec.SecurityContext.ApparmorProfile = o.Apparmor
- }
- if len(o.PostStartExec) != 0 {
- req.Lifecyle = &apis.ContainerLifecyle{
- PostStart: &apis.ContainerLifecyleHandler{
- Type: apis.ContainerLifecyleHandlerTypeExec,
- Exec: &apis.ContainerLifecyleHandlerExecAction{
- Command: strings.Split(o.PostStartExec, " "),
- },
- },
- }
- }
- if len(o.Caps) != 0 {
- req.Capabilities.Add = strings.Split(o.Caps, ",")
- }
- if len(o.DropCaps) != 0 {
- req.Capabilities.Drop = strings.Split(o.DropCaps, ",")
- }
- for _, env := range o.Env {
- e, err := parseContainerEnv(env)
- if err != nil {
- return nil, errors.Wrapf(err, "parseContainerEnv %s", env)
- }
- req.Envs = append(req.Envs, e)
- }
- if len(o.RootFs) != 0 {
- rootFs, err := parseContainerRootFs(o.RootFs)
- if err != nil {
- return nil, errors.Wrapf(err, "parseContainerRootFs %s", o.RootFs)
- }
- req.RootFs = rootFs
- }
- for _, vmStr := range o.VolumeMount {
- vm, err := parseContainerVolumeMount(vmStr)
- if err != nil {
- return nil, errors.Wrapf(err, "parseContainerVolumeMount %s", vmStr)
- }
- req.VolumeMounts = append(req.VolumeMounts, vm)
- }
- devs := make([]*computeapi.ContainerDevice, len(o.Device))
- for idx, devStr := range o.Device {
- dev, err := parseContainerDevice(devStr)
- if err != nil {
- return nil, errors.Wrap(err, "parseContainerDevice")
- }
- devs[idx] = dev
- }
- req.Devices = devs
- return req, nil
- }
- type ContainerCreateOptions struct {
- ContainerCreateCommonOptions
- AutoStart bool `help:"Auto start container" json:"auto_start"`
- PODID string `help:"Name or id of server pod" json:"-"`
- NAME string `help:"Name of container" json:"-"`
- }
- func (o *ContainerCreateOptions) Params() (jsonutils.JSONObject, error) {
- spec, err := o.getCreateSpec()
- if err != nil {
- return nil, errors.Wrap(err, "get container create spec")
- }
- req := computeapi.ContainerCreateInput{
- GuestId: o.PODID,
- Spec: *spec,
- AutoStart: o.AutoStart,
- }
- req.Name = o.NAME
- return jsonutils.Marshal(req), nil
- }
- func parseContainerEnv(env string) (*apis.ContainerKeyValue, error) {
- kv := strings.Split(env, "=")
- if len(kv) != 2 {
- return nil, errors.Errorf("invalid env: %q", env)
- }
- return &apis.ContainerKeyValue{
- Key: kv[0],
- Value: kv[1],
- }, nil
- }
- func parseContainerRootFs(rootFs string) (*apis.ContainerRootfs, error) {
- out := &apis.ContainerRootfs{
- Type: apis.CONTAINER_VOLUME_MOUNT_TYPE_DISK,
- Disk: &apis.ContainerVolumeMountDisk{},
- }
- for _, seg := range strings.Split(rootFs, ",") {
- info := strings.Split(seg, "=")
- if len(info) != 2 {
- return nil, errors.Errorf("invalid option %s", seg)
- }
- key := info[0]
- val := info[1]
- switch key {
- case "disk_index":
- index, err := strconv.Atoi(val)
- if err != nil {
- return nil, errors.Wrapf(err, "wrong disk_index %s", val)
- }
- out.Disk.Index = &index
- case "disk_id":
- out.Disk.Id = val
- case "sub_dir", "sub_directory":
- out.Disk.SubDirectory = val
- }
- }
- return out, nil
- }
- func parseContainerVolumeMount(vmStr string) (*apis.ContainerVolumeMount, error) {
- vm := &apis.ContainerVolumeMount{}
- for _, seg := range strings.Split(vmStr, ",") {
- info := strings.Split(seg, "=")
- if len(info) != 2 {
- return nil, errors.Errorf("invalid option %s", seg)
- }
- key := info[0]
- val := info[1]
- switch key {
- case "read_only", "ro", "readonly":
- if strings.ToLower(val) == "true" {
- vm.ReadOnly = true
- }
- case "fs_user":
- uId, err := strconv.Atoi(val)
- if err != nil {
- return nil, errors.Wrapf(err, "invalid fs_user %s", val)
- }
- uId64 := int64(uId)
- vm.FsUser = &uId64
- case "fs_group":
- gId, err := strconv.Atoi(val)
- if err != nil {
- return nil, errors.Wrapf(err, "invalid fs_group %s", val)
- }
- gId64 := int64(gId)
- vm.FsGroup = &gId64
- case "mount_path", "mount":
- vm.MountPath = val
- case "host_path":
- if vm.HostPath == nil {
- vm.HostPath = &apis.ContainerVolumeMountHostPath{}
- }
- vm.Type = apis.CONTAINER_VOLUME_MOUNT_TYPE_HOST_PATH
- vm.HostPath.Path = val
- case "host_type":
- if vm.HostPath == nil {
- vm.HostPath = &apis.ContainerVolumeMountHostPath{}
- }
- vm.HostPath.Type = apis.ContainerVolumeMountHostPathType(val)
- case "disk_index":
- vm.Type = apis.CONTAINER_VOLUME_MOUNT_TYPE_DISK
- if vm.Disk == nil {
- vm.Disk = &apis.ContainerVolumeMountDisk{}
- }
- index, err := strconv.Atoi(val)
- if err != nil {
- return nil, errors.Wrapf(err, "wrong disk_index %s", val)
- }
- vm.Disk.Index = &index
- case "disk_id":
- vm.Type = apis.CONTAINER_VOLUME_MOUNT_TYPE_DISK
- if vm.Disk == nil {
- vm.Disk = &apis.ContainerVolumeMountDisk{}
- }
- vm.Disk.Id = val
- case "disk_subdir", "disk_sub_dir", "disk_sub_directory":
- vm.Type = apis.CONTAINER_VOLUME_MOUNT_TYPE_DISK
- if vm.Disk == nil {
- vm.Disk = &apis.ContainerVolumeMountDisk{}
- }
- vm.Disk.SubDirectory = val
- case "disk_storage_size_file", "disk_ssf":
- vm.Type = apis.CONTAINER_VOLUME_MOUNT_TYPE_DISK
- if vm.Disk == nil {
- vm.Disk = &apis.ContainerVolumeMountDisk{}
- }
- vm.Disk.StorageSizeFile = val
- case "case_insensitive_paths", "casefold_paths":
- vm.Disk.CaseInsensitivePaths = strings.Split(val, ",")
- case "overlay":
- if vm.Disk == nil {
- vm.Disk = &apis.ContainerVolumeMountDisk{}
- }
- vm.Disk.Overlay = &apis.ContainerVolumeMountDiskOverlay{
- LowerDir: strings.Split(val, ":"),
- }
- case "overlay_disk_image":
- if strings.ToLower(val) == "true" {
- if vm.Disk == nil {
- vm.Disk = &apis.ContainerVolumeMountDisk{}
- }
- vm.Disk.Overlay = &apis.ContainerVolumeMountDiskOverlay{
- UseDiskImage: true,
- }
- }
- case "text_file":
- content, err := os.ReadFile(val)
- if err != nil {
- return nil, errors.Wrapf(err, "read file %s", val)
- }
- vm.Type = apis.CONTAINER_VOLUME_MOUNT_TYPE_TEXT
- vm.Text = &apis.ContainerVolumeMountText{
- Content: string(content),
- }
- case "cephfs":
- vm.Type = apis.CONTAINER_VOLUME_MOUNT_TYPE_CEPHF_FS
- vm.CephFS = &apis.ContainerVolumeMountCephFS{
- Id: val,
- }
- }
- }
- return vm, nil
- }
- type ContainerIdsOptions struct {
- ID []string `help:"ID of containers to operate" metavar:"CONTAINER" json:"-"`
- }
- func (o *ContainerIdsOptions) GetIds() []string {
- return o.ID
- }
- func (o *ContainerIdsOptions) Params() (jsonutils.JSONObject, error) {
- return nil, nil
- }
- type ContainerStopOptions struct {
- ContainerIdsOptions
- Timeout int `help:"Stopping timeout" json:"timeout"`
- Force bool `help:"Force stop container" json:"force"`
- }
- func (o *ContainerStopOptions) Params() (jsonutils.JSONObject, error) {
- return jsonutils.Marshal(o), nil
- }
- type ContainerStartOptions struct {
- ContainerIdsOptions
- }
- type ContainerRestartOptions struct {
- ContainerIdsOptions
- Timeout int `help:"Stopping timeout" json:"timeout"`
- Force bool `help:"Force stop container" json:"force"`
- }
- func (o *ContainerRestartOptions) Params() (jsonutils.JSONObject, error) {
- return jsonutils.Marshal(o), nil
- }
- type ContainerSaveVolumeMountImage struct {
- options.ResourceIdOptions
- IMAGENAME string `help:"Image name"`
- INDEX int `help:"Index of volume mount"`
- GenerateName string `help:"Generate image name automatically"`
- Notes string `help:"Extra notes of the image"`
- UsedByPostOverlay bool `help:"Used by voluem mount post-overlay"`
- Dirs []string `help:"Internal directories"`
- }
- func (o ContainerSaveVolumeMountImage) Params() (jsonutils.JSONObject, error) {
- return jsonutils.Marshal(&computeapi.ContainerSaveVolumeMountToImageInput{
- Name: o.IMAGENAME,
- GenerateName: o.GenerateName,
- Notes: o.Notes,
- Index: o.INDEX,
- Dirs: o.Dirs,
- UsedByPostOverlay: o.UsedByPostOverlay,
- }), nil
- }
- type ContainerExecOptions struct {
- ServerIdOptions
- // Tty bool `help:"Using tty" short-token:"t"`
- COMMAND string
- Args []string
- }
- func (o *ContainerExecOptions) ToAPIInput() *compute.ContainerExecInput {
- cmd := []string{o.COMMAND}
- cmd = append(cmd, o.Args...)
- return &compute.ContainerExecInput{
- Command: cmd,
- Tty: true,
- Stdin: os.Stdin,
- Stdout: os.Stdout,
- Stderr: os.Stderr,
- }
- }
- func (o *ContainerExecOptions) Params() (jsonutils.JSONObject, error) {
- return jsonutils.Marshal(o.ToAPIInput()), nil
- }
- type ContainerSetResourcesLimitOptions struct {
- ContainerIdsOptions
- DisableLimitCheck bool `help:"disable limit check"`
- CpuCfsQuota float64 `help:"cpu cfs quota. e.g.:0.5 equals 0.5*100000"`
- //MemoryLimitMb int64 `help:"memory limit MB"`
- PidsMax int `help:"pids max"`
- DeviceAllow []string `help:"devices allow"`
- }
- func (o *ContainerSetResourcesLimitOptions) Params() (jsonutils.JSONObject, error) {
- limit := &computeapi.ContainerResourcesSetInput{}
- if o.CpuCfsQuota > 0 {
- limit.CpuCfsQuota = &o.CpuCfsQuota
- }
- //if o.MemoryLimitMb > 0 {
- // limit.MemoryLimitMB = &o.MemoryLimitMb
- //}
- if o.PidsMax > 0 {
- limit.PidsMax = &o.PidsMax
- }
- if len(o.DeviceAllow) > 0 {
- limit.DevicesAllow = o.DeviceAllow
- }
- limit.DisableLimitCheck = o.DisableLimitCheck
- return jsonutils.Marshal(limit), nil
- }
- type ContainerExecSyncOptions struct {
- ServerIdOptions
- COMMAND string
- Args []string
- Timeout int64
- }
- func (o *ContainerExecSyncOptions) Params() (jsonutils.JSONObject, error) {
- cmd := []string{o.COMMAND}
- cmd = append(cmd, o.Args...)
- return jsonutils.Marshal(&computeapi.ContainerExecSyncInput{
- Command: cmd,
- Timeout: o.Timeout,
- }), nil
- }
- type ContainerLogOptions struct {
- ServerIdOptions
- Since string `help:"Only return logs newer than a relative duration like 5s, 2m, or 3h" json:"since"`
- Follow bool `help:"Follow log output" short-token:"f" json:"follow"`
- Tail int64 `help:"Lines of recent log file to display" json:"tail"`
- Timestamps bool `help:"Show timestamps on each line in the log output" json:"timestamps"`
- LimitBytes int64 `help:"Maximum amount of bytes that can be used." json:"limitBytes"`
- }
- func (o *ContainerLogOptions) Params() (jsonutils.JSONObject, error) {
- input, err := o.ToAPIInput()
- if err != nil {
- return nil, err
- }
- return jsonutils.Marshal(input), nil
- }
- func (o *ContainerLogOptions) ToAPIInput() (*computeapi.PodLogOptions, error) {
- opt := &computeapi.PodLogOptions{
- Follow: o.Follow,
- Timestamps: o.Timestamps,
- }
- if o.LimitBytes > 0 {
- opt.LimitBytes = &o.LimitBytes
- }
- if o.Tail > 0 {
- opt.TailLines = &o.Tail
- }
- if len(o.Since) > 0 {
- dur, err := time.ParseDuration(o.Since)
- if err != nil {
- return nil, errors.Wrapf(err, "invalid time duration: %s, shoud like 300ms, 1.5h or 2h45m", o.Since)
- }
- sec := int64(dur.Round(time.Second).Seconds())
- opt.SinceSeconds = &sec
- }
- return opt, nil
- }
- type ContainerCommitOptions struct {
- ServerIdOptions
- RegistryId string `help:"Registry ID from kubeserver"`
- ImageName string `help:"Image name"`
- Tag string `help:"Tag"`
- ExternalRegistryUrl string `help:"External registry URL, e.g.: registry.cn-beijing.aliyuncs.com/yunionio"`
- ExternalRegistryUsername string `help:"External registry username"`
- ExternalRegistryPassword string `help:"External registry password"`
- }
- func (o *ContainerCommitOptions) Params() (jsonutils.JSONObject, error) {
- input := &computeapi.ContainerCommitInput{
- RegistryId: o.RegistryId,
- ImageName: o.ImageName,
- Tag: o.Tag,
- ExternalRegistry: &computeapi.ContainerCommitExternalRegistry{
- Auth: &apis.ContainerPullImageAuthConfig{},
- },
- }
- if o.ExternalRegistryUrl != "" {
- input.ExternalRegistry.Url = o.ExternalRegistryUrl
- }
- if o.ExternalRegistryUsername != "" {
- input.ExternalRegistry.Auth.Username = o.ExternalRegistryUsername
- }
- if o.ExternalRegistryPassword != "" {
- input.ExternalRegistry.Auth.Password = o.ExternalRegistryPassword
- }
- return jsonutils.Marshal(input), nil
- }
- type ContainerAddVolumeMountPostOverlayOptions struct {
- ServerIdOptions
- INDEX int `help:"INDEX of volume mount"`
- MountDesc []string `help:"Mount description, <host_lower_dir>:<container_target_dir>" short-token:"m"`
- Image []string `help:"Image name or id"`
- }
- func (o *ContainerAddVolumeMountPostOverlayOptions) Params() (jsonutils.JSONObject, error) {
- input := &computeapi.ContainerVolumeMountAddPostOverlayInput{
- Index: o.INDEX,
- PostOverlay: make([]*apis.ContainerVolumeMountDiskPostOverlay, 0),
- }
- for _, md := range o.MountDesc {
- segs := strings.Split(md, ":")
- if len(segs) != 2 {
- return nil, errors.Errorf("invalid mount description: %s", md)
- }
- lowerDir := segs[0]
- containerTargetDir := segs[1]
- input.PostOverlay = append(input.PostOverlay, &apis.ContainerVolumeMountDiskPostOverlay{
- HostLowerDir: []string{lowerDir},
- ContainerTargetDir: containerTargetDir,
- })
- }
- if len(o.Image) != 0 {
- for _, img := range o.Image {
- input.PostOverlay = append(input.PostOverlay, &apis.ContainerVolumeMountDiskPostOverlay{
- Image: &apis.ContainerVolumeMountDiskPostImageOverlay{
- Id: img,
- },
- })
- }
- }
- return jsonutils.Marshal(input), nil
- }
- type ContainerRemoveVolumeMountPostOverlayOptions struct {
- ContainerAddVolumeMountPostOverlayOptions
- ClearLayers bool `help:"clear overlay upper and work layers"`
- UseLazy bool `help:"use lazy umount"`
- }
- func (o *ContainerRemoveVolumeMountPostOverlayOptions) Params() (jsonutils.JSONObject, error) {
- params, err := o.ContainerAddVolumeMountPostOverlayOptions.Params()
- if err != nil {
- return nil, err
- }
- if o.ClearLayers {
- params.(*jsonutils.JSONDict).Add(jsonutils.JSONTrue, "clear_layers")
- }
- if o.UseLazy {
- params.(*jsonutils.JSONDict).Add(jsonutils.JSONTrue, "use_lazy")
- }
- return params, nil
- }
- type ContainerCopyOptions struct {
- SRC string `help:"Local path or file name, or cotnainer:path, e.g. /etc/hots or ctr-0:/etc/hosts"`
- DST string `help:"Local path or file name, or cotnainer:path, e.g. /etc/hots or ctr-0:/etc/hosts"`
- RawFile bool `help:"copy the file as raw data, if false, requires tar in executive path in container and host"`
- }
- type ContainerCopyFromOptions struct {
- CONTAINER_ID_PATH string `help:"container id and the file path in the container, separated by ':', e.g. ctr-0:/etc/hosts"`
- DST_PATH string `help:"Local destination path or file name"`
- RawFile bool `help:"copy the file as raw data, if false, requires tar in executive path in container and host"`
- }
|