mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-10-21 11:58:41 +00:00
- Add kata-runtime - Add unit test - Add Makefile to build cli Fixes: #33 Signed-off-by: Julio Montes <julio.montes@intel.com> Signed-off-by: James O. D. Hunt <james.o.hunt@intel.com> Signed-off-by: Jose Carlos Venegas Munoz <jose.carlos.venegas.munoz@intel.com>
352 lines
8.5 KiB
Go
352 lines
8.5 KiB
Go
// Copyright (c) 2017 Intel Corporation
|
|
//
|
|
// 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 main
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"syscall"
|
|
|
|
vc "github.com/kata-containers/runtime/virtcontainers"
|
|
"github.com/kata-containers/runtime/virtcontainers/pkg/oci"
|
|
"github.com/opencontainers/runc/libcontainer/utils"
|
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// Contants related to cgroup memory directory
|
|
const (
|
|
cgroupsTasksFile = "tasks"
|
|
cgroupsProcsFile = "cgroup.procs"
|
|
cgroupsDirMode = os.FileMode(0750)
|
|
cgroupsFileMode = os.FileMode(0640)
|
|
|
|
// Filesystem type corresponding to CGROUP_SUPER_MAGIC as listed
|
|
// here: http://man7.org/linux/man-pages/man2/statfs.2.html
|
|
cgroupFsType = 0x27e0eb
|
|
)
|
|
|
|
var errNeedLinuxResource = errors.New("Linux resource cannot be empty")
|
|
|
|
var cgroupsDirPath string
|
|
|
|
var procMountInfo = "/proc/self/mountinfo"
|
|
|
|
// getContainerInfo returns the container status and its pod ID.
|
|
// It internally expands the container ID from the prefix provided.
|
|
func getContainerInfo(containerID string) (vc.ContainerStatus, string, error) {
|
|
// container ID MUST be provided.
|
|
if containerID == "" {
|
|
return vc.ContainerStatus{}, "", fmt.Errorf("Missing container ID")
|
|
}
|
|
|
|
podStatusList, err := vci.ListPod()
|
|
if err != nil {
|
|
return vc.ContainerStatus{}, "", err
|
|
}
|
|
|
|
for _, podStatus := range podStatusList {
|
|
for _, containerStatus := range podStatus.ContainersStatus {
|
|
if containerStatus.ID == containerID {
|
|
return containerStatus, podStatus.ID, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// Not finding a container should not trigger an error as
|
|
// getContainerInfo is used for checking the existence and
|
|
// the absence of a container ID.
|
|
return vc.ContainerStatus{}, "", nil
|
|
}
|
|
|
|
func getExistingContainerInfo(containerID string) (vc.ContainerStatus, string, error) {
|
|
cStatus, podID, err := getContainerInfo(containerID)
|
|
if err != nil {
|
|
return vc.ContainerStatus{}, "", err
|
|
}
|
|
|
|
// container ID MUST exist.
|
|
if cStatus.ID == "" {
|
|
return vc.ContainerStatus{}, "", fmt.Errorf("Container ID (%v) does not exist", containerID)
|
|
}
|
|
|
|
return cStatus, podID, nil
|
|
}
|
|
|
|
func validCreateParams(containerID, bundlePath string) (string, error) {
|
|
// container ID MUST be provided.
|
|
if containerID == "" {
|
|
return "", fmt.Errorf("Missing container ID")
|
|
}
|
|
|
|
// container ID MUST be unique.
|
|
cStatus, _, err := getContainerInfo(containerID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if cStatus.ID != "" {
|
|
return "", fmt.Errorf("ID already in use, unique ID should be provided")
|
|
}
|
|
|
|
// bundle path MUST be provided.
|
|
if bundlePath == "" {
|
|
return "", fmt.Errorf("Missing bundle path")
|
|
}
|
|
|
|
// bundle path MUST be valid.
|
|
fileInfo, err := os.Stat(bundlePath)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Invalid bundle path '%s': %s", bundlePath, err)
|
|
}
|
|
if fileInfo.IsDir() == false {
|
|
return "", fmt.Errorf("Invalid bundle path '%s', it should be a directory", bundlePath)
|
|
}
|
|
|
|
resolved, err := resolvePath(bundlePath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return resolved, nil
|
|
}
|
|
|
|
// processCgroupsPath process the cgroups path as expected from the
|
|
// OCI runtime specification. It returns a list of complete paths
|
|
// that should be created and used for every specified resource.
|
|
func processCgroupsPath(ociSpec oci.CompatOCISpec, isPod bool) ([]string, error) {
|
|
var cgroupsPathList []string
|
|
|
|
if ociSpec.Linux.CgroupsPath == "" {
|
|
return []string{}, nil
|
|
}
|
|
|
|
if ociSpec.Linux.Resources == nil {
|
|
return []string{}, nil
|
|
}
|
|
|
|
if ociSpec.Linux.Resources.Memory != nil {
|
|
memCgroupsPath, err := processCgroupsPathForResource(ociSpec, "memory", isPod)
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
|
|
if memCgroupsPath != "" {
|
|
cgroupsPathList = append(cgroupsPathList, memCgroupsPath)
|
|
}
|
|
}
|
|
|
|
if ociSpec.Linux.Resources.CPU != nil {
|
|
cpuCgroupsPath, err := processCgroupsPathForResource(ociSpec, "cpu", isPod)
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
|
|
if cpuCgroupsPath != "" {
|
|
cgroupsPathList = append(cgroupsPathList, cpuCgroupsPath)
|
|
}
|
|
}
|
|
|
|
if ociSpec.Linux.Resources.Pids != nil {
|
|
pidsCgroupsPath, err := processCgroupsPathForResource(ociSpec, "pids", isPod)
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
|
|
if pidsCgroupsPath != "" {
|
|
cgroupsPathList = append(cgroupsPathList, pidsCgroupsPath)
|
|
}
|
|
}
|
|
|
|
if ociSpec.Linux.Resources.BlockIO != nil {
|
|
blkIOCgroupsPath, err := processCgroupsPathForResource(ociSpec, "blkio", isPod)
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
|
|
if blkIOCgroupsPath != "" {
|
|
cgroupsPathList = append(cgroupsPathList, blkIOCgroupsPath)
|
|
}
|
|
}
|
|
|
|
return cgroupsPathList, nil
|
|
}
|
|
|
|
func processCgroupsPathForResource(ociSpec oci.CompatOCISpec, resource string, isPod bool) (string, error) {
|
|
if resource == "" {
|
|
return "", errNeedLinuxResource
|
|
}
|
|
|
|
var err error
|
|
cgroupsDirPath, err = getCgroupsDirPath(procMountInfo)
|
|
if err != nil {
|
|
return "", fmt.Errorf("get CgroupsDirPath error: %s", err)
|
|
}
|
|
|
|
// Relative cgroups path provided.
|
|
if filepath.IsAbs(ociSpec.Linux.CgroupsPath) == false {
|
|
return filepath.Join(cgroupsDirPath, resource, ociSpec.Linux.CgroupsPath), nil
|
|
}
|
|
|
|
// Absolute cgroups path provided.
|
|
var cgroupMount specs.Mount
|
|
cgroupMountFound := false
|
|
for _, mount := range ociSpec.Mounts {
|
|
if mount.Type == "cgroup" {
|
|
cgroupMount = mount
|
|
cgroupMountFound = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !cgroupMountFound {
|
|
// According to the OCI spec, an absolute path should be
|
|
// interpreted as relative to the system cgroup mount point
|
|
// when there is no cgroup mount point.
|
|
return filepath.Join(cgroupsDirPath, resource, ociSpec.Linux.CgroupsPath), nil
|
|
}
|
|
|
|
if cgroupMount.Destination == "" {
|
|
return "", fmt.Errorf("cgroupsPath is absolute, cgroup mount destination cannot be empty")
|
|
}
|
|
|
|
cgroupPath := filepath.Join(cgroupMount.Destination, resource)
|
|
|
|
// It is not an error to have this cgroup not mounted. It is usually
|
|
// due to an old kernel version with missing support for specific
|
|
// cgroups.
|
|
fields := logrus.Fields{
|
|
"path": cgroupPath,
|
|
"type": "cgroup",
|
|
}
|
|
|
|
if !isCgroupMounted(cgroupPath) {
|
|
kataLog.WithFields(fields).Info("path not mounted")
|
|
return "", nil
|
|
}
|
|
|
|
kataLog.WithFields(fields).Info("path mounted")
|
|
|
|
return filepath.Join(cgroupPath, ociSpec.Linux.CgroupsPath), nil
|
|
}
|
|
|
|
func isCgroupMounted(cgroupPath string) bool {
|
|
var statFs syscall.Statfs_t
|
|
|
|
if err := syscall.Statfs(cgroupPath, &statFs); err != nil {
|
|
return false
|
|
}
|
|
|
|
if statFs.Type != int64(cgroupFsType) {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func setupConsole(consolePath, consoleSockPath string) (string, error) {
|
|
if consolePath != "" {
|
|
return consolePath, nil
|
|
}
|
|
|
|
if consoleSockPath == "" {
|
|
return "", nil
|
|
}
|
|
|
|
console, err := newConsole()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer console.master.Close()
|
|
|
|
// Open the socket path provided by the caller
|
|
conn, err := net.Dial("unix", consoleSockPath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
uConn, ok := conn.(*net.UnixConn)
|
|
if !ok {
|
|
return "", fmt.Errorf("casting to *net.UnixConn failed")
|
|
}
|
|
|
|
socket, err := uConn.File()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Send the parent fd through the provided socket
|
|
if err := utils.SendFd(socket, console.master.Name(), console.master.Fd()); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return console.slavePath, nil
|
|
}
|
|
|
|
func noNeedForOutput(detach bool, tty bool) bool {
|
|
if !detach {
|
|
return false
|
|
}
|
|
|
|
if !tty {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func getCgroupsDirPath(mountInfoFile string) (string, error) {
|
|
if cgroupsDirPath != "" {
|
|
return cgroupsDirPath, nil
|
|
}
|
|
|
|
f, err := os.Open(mountInfoFile)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer f.Close()
|
|
|
|
var cgroupRootPath string
|
|
scanner := bufio.NewScanner(f)
|
|
for scanner.Scan() {
|
|
text := scanner.Text()
|
|
index := strings.Index(text, " - ")
|
|
if index < 0 {
|
|
continue
|
|
}
|
|
fields := strings.Split(text, " ")
|
|
postSeparatorFields := strings.Fields(text[index+3:])
|
|
numPostFields := len(postSeparatorFields)
|
|
|
|
if len(fields) < 5 || postSeparatorFields[0] != "cgroup" || numPostFields < 3 {
|
|
continue
|
|
}
|
|
|
|
cgroupRootPath = filepath.Dir(fields[4])
|
|
break
|
|
}
|
|
|
|
if _, err = os.Stat(cgroupRootPath); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return cgroupRootPath, nil
|
|
}
|