diff --git a/virtcontainers/types/sandbox.go b/virtcontainers/types/sandbox.go new file mode 100644 index 0000000000..bbd5843d69 --- /dev/null +++ b/virtcontainers/types/sandbox.go @@ -0,0 +1,275 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package types + +import ( + "fmt" + "strings" +) + +// stateString is a string representing a sandbox state. +type stateString string + +const ( + // StateReady represents a sandbox/container that's ready to be run + StateReady stateString = "ready" + + // StateRunning represents a sandbox/container that's currently running. + StateRunning stateString = "running" + + // StatePaused represents a sandbox/container that has been paused. + StatePaused stateString = "paused" + + // StateStopped represents a sandbox/container that has been stopped. + StateStopped stateString = "stopped" +) + +// State is a sandbox state structure. +type State struct { + State stateString `json:"state"` + + BlockDeviceID string + // Index of the block device passed to hypervisor. + BlockIndex int `json:"blockIndex"` + + // File system of the rootfs incase it is block device + Fstype string `json:"fstype"` + + // Pid is the process id of the sandbox container which is the first + // container to be started. + Pid int `json:"pid"` + + // GuestMemoryBlockSizeMB is the size of memory block of guestos + GuestMemoryBlockSizeMB uint32 `json:"guestMemoryBlockSize"` +} + +// valid checks that the sandbox state is valid. +func (state *State) valid() bool { + for _, validState := range []stateString{StateReady, StateRunning, StatePaused, StateStopped} { + if state.State == validState { + return true + } + } + + return false +} + +// validTransition returns an error if we want to move to +// an unreachable state. +func (state *State) validTransition(oldState stateString, newState stateString) error { + if state.State != oldState { + return fmt.Errorf("Invalid state %s (Expecting %s)", state.State, oldState) + } + + switch state.State { + case StateReady: + if newState == StateRunning || newState == StateStopped { + return nil + } + + case StateRunning: + if newState == StatePaused || newState == StateStopped { + return nil + } + + case StatePaused: + if newState == StateRunning || newState == StateStopped { + return nil + } + + case StateStopped: + if newState == StateRunning { + return nil + } + } + + return fmt.Errorf("Can not move from %s to %s", + state.State, newState) +} + +// Volume is a shared volume between the host and the VM, +// defined by its mount tag and its host path. +type Volume struct { + // MountTag is a label used as a hint to the guest. + MountTag string + + // HostPath is the host filesystem path for this volume. + HostPath string +} + +// Volumes is a Volume list. +type Volumes []Volume + +// Set assigns volume values from string to a Volume. +func (v *Volumes) Set(volStr string) error { + if volStr == "" { + return fmt.Errorf("volStr cannot be empty") + } + + volSlice := strings.Split(volStr, " ") + const expectedVolLen = 2 + const volDelimiter = ":" + + for _, vol := range volSlice { + volArgs := strings.Split(vol, volDelimiter) + + if len(volArgs) != expectedVolLen { + return fmt.Errorf("Wrong string format: %s, expecting only %v parameters separated with %q", + vol, expectedVolLen, volDelimiter) + } + + if volArgs[0] == "" || volArgs[1] == "" { + return fmt.Errorf("Volume parameters cannot be empty") + } + + volume := Volume{ + MountTag: volArgs[0], + HostPath: volArgs[1], + } + + *v = append(*v, volume) + } + + return nil +} + +// String converts a Volume to a string. +func (v *Volumes) String() string { + var volSlice []string + + for _, volume := range *v { + volSlice = append(volSlice, fmt.Sprintf("%s:%s", volume.MountTag, volume.HostPath)) + } + + return strings.Join(volSlice, " ") +} + +// Socket defines a socket to communicate between +// the host and any process inside the VM. +type Socket struct { + DeviceID string + ID string + HostPath string + Name string +} + +// Sockets is a Socket list. +type Sockets []Socket + +// Set assigns socket values from string to a Socket. +func (s *Sockets) Set(sockStr string) error { + if sockStr == "" { + return fmt.Errorf("sockStr cannot be empty") + } + + sockSlice := strings.Split(sockStr, " ") + const expectedSockCount = 4 + const sockDelimiter = ":" + + for _, sock := range sockSlice { + sockArgs := strings.Split(sock, sockDelimiter) + + if len(sockArgs) != expectedSockCount { + return fmt.Errorf("Wrong string format: %s, expecting only %v parameters separated with %q", sock, expectedSockCount, sockDelimiter) + } + + for _, a := range sockArgs { + if a == "" { + return fmt.Errorf("Socket parameters cannot be empty") + } + } + + socket := Socket{ + DeviceID: sockArgs[0], + ID: sockArgs[1], + HostPath: sockArgs[2], + Name: sockArgs[3], + } + + *s = append(*s, socket) + } + + return nil +} + +// String converts a Socket to a string. +func (s *Sockets) String() string { + var sockSlice []string + + for _, sock := range *s { + sockSlice = append(sockSlice, fmt.Sprintf("%s:%s:%s:%s", sock.DeviceID, sock.ID, sock.HostPath, sock.Name)) + } + + return strings.Join(sockSlice, " ") +} + +// EnvVar is a key/value structure representing a command +// environment variable. +type EnvVar struct { + Var string + Value string +} + +// LinuxCapabilities specify the capabilities to keep when executing +// the process inside the container. +type LinuxCapabilities struct { + // Bounding is the set of capabilities checked by the kernel. + Bounding []string + // Effective is the set of capabilities checked by the kernel. + Effective []string + // Inheritable is the capabilities preserved across execve. + Inheritable []string + // Permitted is the limiting superset for effective capabilities. + Permitted []string + // Ambient is the ambient set of capabilities that are kept. + Ambient []string +} + +// Cmd represents a command to execute in a running container. +type Cmd struct { + Args []string + Envs []EnvVar + SupplementaryGroups []string + + // Note that these fields *MUST* remain as strings. + // + // The reason being that we want runtimes to be able to support CLI + // operations like "exec --user=". That option allows the + // specification of a user (either as a string username or a numeric + // UID), and may optionally also include a group (groupame or GID). + // + // Since this type is the interface to allow the runtime to specify + // the user and group the workload can run as, these user and group + // fields cannot be encoded as integer values since that would imply + // the runtime itself would need to perform a UID/GID lookup on the + // user-specified username/groupname. But that isn't practically + // possible given that to do so would require the runtime to access + // the image to allow it to interrogate the appropriate databases to + // convert the username/groupnames to UID/GID values. + // + // Note that this argument applies solely to the _runtime_ supporting + // a "--user=" option when running in a "standalone mode" - there is + // no issue when the runtime is called by a container manager since + // all the user and group mapping is handled by the container manager + // and specified to the runtime in terms of UID/GID's in the + // configuration file generated by the container manager. + User string + PrimaryGroup string + WorkDir string + Console string + Capabilities LinuxCapabilities + + Interactive bool + Detach bool + NoNewPrivileges bool +} + +// Resources describes VM resources configuration. +type Resources struct { + // Memory is the amount of available memory in MiB. + Memory uint + MemorySlots uint8 +}