taleintervenor dfaef9d393 syscontainer-tools: update license to Mulan PSL v2
reason: update license to Mulan PSL v2

Signed-off-by: taleintervenor <taleintervenor@aliyun.com>
2020-04-27 14:54:17 +08:00

250 lines
7.1 KiB
Go

// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
// syscontainer-tools is licensed under the Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
// PURPOSE.
// See the Mulan PSL v2 for more details.
// Description: container config operation
// Author: zhangwei
// Create: 2018-01-18
package container
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
var (
// deviceHookLock is the default isulad container file lock name
deviceHookLock = ".device_hook.lock"
// restrictedNameChars collects the characters allowed to represent a name, normally used to validate container and volume names.
restrictedNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.-]`
// restrictedNamePattern is a regular expression to validate names against the collection of restricted characters.
restrictedNamePattern = regexp.MustCompile(`^/?` + restrictedNameChars + `+$`)
)
// Container is a structure which contains the basic config of isulad containers
type Container struct {
pid int
containerID string
containerPath string
name string
spec *specs.Spec
lock *os.File
}
// New will create a container via a container name
func New(name string) (*Container, error) {
if !restrictedNamePattern.MatchString(name) {
return nil, fmt.Errorf("Invalid container name (%s), only %s are allowed", name, restrictedNameChars)
}
graphDriverPath, err := getIsuladGraphDriverPath()
var id, storagePath string
var pid int
var spec *specs.Spec
storagePath = filepath.Join(graphDriverPath, "engines", "lcr")
id, err = getIsuladContainerID(name)
if err != nil {
return nil, err
}
pid, err = getIsuladContainerPid(name)
if err != nil {
return nil, err
}
spec, err = getIsuladContainerSpec(id)
if err != nil {
logrus.Warnf("fail to get isulad container %v spec: %v", id, err)
}
container := &Container{
pid: pid,
name: name,
containerID: id,
spec: spec,
containerPath: filepath.Join(storagePath, id),
}
return container, nil
}
// Pid returns the pid of the container
func (c *Container) Pid() int {
return c.pid
}
// ContainerID returns the container id
func (c *Container) ContainerID() string {
return c.containerID
}
// Name returns the container name input by user.
func (c *Container) Name() string {
return c.name
}
// ContainerPath returns the container config storage path.
func (c *Container) ContainerPath() string {
return c.containerPath
}
// NetNsPath returns the net namespace path of the container
func (c *Container) NetNsPath() string {
return fmt.Sprintf("/proc/%d/ns/net", c.pid)
}
// Lock uses file lock to lock the container
// to make sure only one handler could access the container resource
func (c *Container) Lock() error {
fileName := filepath.Join(c.ContainerPath(), deviceHookLock)
f, err := os.OpenFile(fileName, os.O_RDONLY|os.O_CREATE, 0600)
if err != nil {
return err
}
// FileLock will be released at 3 conditions:
// 1. process to unlock manully.
// 2. Close the opened fd.
// 3. process died without call unlock. kernel will close the file and release the lock.
// LOCK_EX means only one process could lock it at one time.
// LOCK_NB is not set, using block mode.
if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil {
f.Close()
return err
}
c.lock = f
return nil
}
// Unlock will release the file lock
func (c *Container) Unlock() error {
defer c.lock.Close()
return unix.Flock(int(c.lock.Fd()), unix.LOCK_UN)
}
// GetCgroupPath returns the cgroup-parent segment of the container.
// For isulad container, it is a configurable segment.
func (c *Container) GetCgroupPath() (string, error) {
cmd := exec.Command("isula", "inspect", "-f", "{{json .HostConfig.CgroupParent}}", c.name)
out, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("%s: %v", string(out), err)
}
cgroupPath := strings.Trim(string(out), "\n")
if len(cgroupPath) >= 2 {
cgroupPath = cgroupPath[1 : len(cgroupPath)-1]
}
if cgroupPath == "" {
// by default, the cgroup path is "/isulad/<id>"
cgroupPath = "/isulad"
}
cgroupPath = filepath.Join(cgroupPath, c.containerID)
return cgroupPath, nil
}
// GetSpec get container spec
func (c *Container) GetSpec() *specs.Spec {
return c.spec
}
// getIsuladContainerID returns the isulad container ID via the container name
func getIsuladContainerID(name string) (string, error) {
cmd := exec.Command("isula", "inspect", "-f", "{{json .Id}}", name)
out, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("%s: %v", string(out), err)
}
return strings.Trim(strings.Trim(string(out), "\n"), "\""), nil
}
// getIsuladContainerPid returns the isulad container process id via the container name
func getIsuladContainerPid(name string) (int, error) {
cmd := exec.Command("isula", "inspect", "-f", "{{json .State.Pid}}", name)
out, err := cmd.CombinedOutput()
if err != nil {
return -1, fmt.Errorf("%s: %v", string(out), err)
}
strPid := strings.Trim(string(out), "\n")
pid, err := strconv.Atoi(strPid)
if err != nil {
return -1, fmt.Errorf("failed to convert %q to int: %v", strPid, err)
}
return pid, nil
}
func getIsuladContainerSpec(id string) (spec *specs.Spec, err error) {
graphDriverPath, err := getIsuladGraphDriverPath()
if err != nil {
return nil, err
}
configPath := fmt.Sprintf("%s/engines/lcr/%s/config.json", graphDriverPath, id)
config, err := os.Open(configPath)
if err != nil {
if os.IsNotExist(err) {
return nil, fmt.Errorf("config file %s not found", configPath)
}
return nil, err
}
defer func() {
if config != nil {
config.Close()
}
}()
if err := json.NewDecoder(config).Decode(&spec); err != nil {
return nil, err
}
return spec, nil
}
func getIsuladGraphDriverPath() (string, error) {
cmd := exec.Command("isula", "info")
out, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("Exec isula info failed: %v", err)
}
// Find "iSulad Root Dir: /xx/xx" line. and out is still has the rest characters.
if index := strings.Index(string(out), "iSulad Root Dir:"); index != -1 {
// Split to array, and the first line is the "iSulad Root Dir"
arr := strings.Split(string(out)[index:], "\n")
// Split to find " /xxx/xxx"
array := strings.Split(arr[0], ":")
if len(array) > 1 {
// Trim all the spaces.
rootdir := strings.Trim(array[1], " ")
return rootdir, nil
}
}
return "", fmt.Errorf("Faild to parse isula info, no \"iSulad Root Dir:\" found")
}
// SetContainerPath set container path
func (c *Container) SetContainerPath(path string) {
c.containerPath = path
return
}
// CheckPidExist check pid exist or not
func (c *Container) CheckPidExist() bool {
if _, err := os.Stat(fmt.Sprintf("/proc/%d", c.Pid())); err != nil {
return false
}
return true
}