reason: update license to Mulan PSL v2 Signed-off-by: taleintervenor <taleintervenor@aliyun.com>
317 lines
7.6 KiB
Go
317 lines
7.6 KiB
Go
// Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved.
|
|
// lxcfs-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: remount command
|
|
// Author: zhangsong
|
|
// Create: 2019-01-18
|
|
|
|
// go base main package
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"lxcfs-tools/libmount"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
|
|
lxcfs_log "github.com/sirupsen/logrus"
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
var recmountContainer = cli.Command{
|
|
Name: "remount",
|
|
Usage: "remount lxcfs to a certain container",
|
|
ArgsUsage: `[options] <container-id>`,
|
|
Description: `You can remount lxcfs to a running container by container id.`,
|
|
|
|
Flags: []cli.Flag{
|
|
cli.BoolFlag{
|
|
Name: "all, a",
|
|
Usage: "remount lxcfs to all running container",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "container-id, c",
|
|
Value: " ",
|
|
Usage: "remount a certain container by container id",
|
|
},
|
|
},
|
|
Action: func(context *cli.Context) {
|
|
if context.NArg() > 0 {
|
|
onfailf("%s: requires none args", context.Command.Name)
|
|
}
|
|
|
|
if err := waitForLxcfs(); err != nil {
|
|
onfail(err)
|
|
}
|
|
|
|
initMountns, err := os.Readlink("/proc/1/ns/mnt")
|
|
if err != nil {
|
|
onfail(fmt.Errorf("read init mount namespace fail: %v", err))
|
|
}
|
|
initUserns, err := os.Readlink("/proc/1/ns/user")
|
|
if err != nil {
|
|
onfail(fmt.Errorf("read init user namespace fail: %v", err))
|
|
}
|
|
if context.Bool("all") {
|
|
lxcfs_log.Info("remount to all containers")
|
|
if err := remountAll(initMountns, initUserns); err != nil {
|
|
onfail(err)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
containerid := context.String("container-id")
|
|
containerid = strings.Replace(containerid, "\n", "", -1)
|
|
if len(containerid) == 1 {
|
|
onfailf("%s: You must choose at least one container", context.Command.Name)
|
|
}
|
|
|
|
if err := remountToContainer(initMountns, initUserns, containerid, "", false); err != nil {
|
|
onfail(err)
|
|
}
|
|
|
|
},
|
|
}
|
|
|
|
var checklxcfs = cli.Command{
|
|
Name: "check-lxcfs",
|
|
Usage: "check whether lxcfs is running",
|
|
ArgsUsage: ` `,
|
|
Description: `do lxcfs check.`,
|
|
Flags: []cli.Flag{},
|
|
|
|
Action: func(context *cli.Context) {
|
|
if context.NArg() > 0 {
|
|
onfailf("%s: don't need any args", context.Command.Name)
|
|
}
|
|
if err := waitForLxcfs(); err != nil {
|
|
onfail(err)
|
|
} else {
|
|
lxcfs_log.Info("lxcfs is running")
|
|
}
|
|
},
|
|
}
|
|
|
|
var execprestart = cli.Command{
|
|
Name: "prestart",
|
|
Usage: "bind mount before lxcfs start ",
|
|
ArgsUsage: ` `,
|
|
Description: `do perstart.`,
|
|
Flags: []cli.Flag{},
|
|
Action: func(context *cli.Context) {
|
|
if context.NArg() > 0 {
|
|
onfailf("%s: don't need any args", context.Command.Name)
|
|
}
|
|
if err := doprestart(); err != nil {
|
|
onfail(err)
|
|
} else {
|
|
lxcfs_log.Info("prestart done")
|
|
}
|
|
},
|
|
}
|
|
|
|
func doprestart() error {
|
|
lxcfs_log.Info("do prestart")
|
|
|
|
if err := syscall.Unmount("/var/lib/lxc/lxcfs", syscall.MNT_DETACH); err == nil {
|
|
lxcfs_log.Warning("releaseMountpoint: umount /var/lib/lxc/lxcfs")
|
|
}
|
|
|
|
if err := syscall.Unmount("/var/lib/lxc", syscall.MNT_DETACH); err == nil {
|
|
lxcfs_log.Warning("releaseMountpoint: umount /var/lib/lxc")
|
|
}
|
|
|
|
prestartparm1 := []string{
|
|
"-B",
|
|
"/var/lib/lxc",
|
|
"/var/lib/lxc",
|
|
}
|
|
|
|
if _, err := execCommond("mount", prestartparm1); err != nil {
|
|
onfail(err)
|
|
}
|
|
prestartparm2 := []string{
|
|
"--make-shared",
|
|
"/var/lib/lxc",
|
|
}
|
|
if _, err := execCommond("mount", prestartparm2); err != nil {
|
|
onfail(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func waitForLxcfs() error {
|
|
count := 0
|
|
// bugfix: wait a bit long time for lxcfs ready
|
|
maxCount := 300
|
|
|
|
for count < maxCount {
|
|
_, err := ioutil.ReadDir("/var/lib/lxc/lxcfs/proc")
|
|
if err != nil {
|
|
time.Sleep(time.Millisecond * 50) // sleep time 50 Millisecond
|
|
} else {
|
|
break
|
|
}
|
|
count++
|
|
}
|
|
|
|
if count == maxCount {
|
|
err := fmt.Errorf("lxcfs is not ready")
|
|
lxcfs_log.Errorf("%v", err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func remountAll(initMountns, initUserns string) error {
|
|
lxcfs_log.Info("begin remount All runing container...")
|
|
out, err := execCommond("isula", []string{"ps", "--format", "{{.ID}} {{.Pid}}"})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
for _, value := range out {
|
|
containerslice := strings.Fields(value)
|
|
if len(containerslice) < 2 {
|
|
continue
|
|
}
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
res := make(chan struct{}, 1)
|
|
go func() {
|
|
if err := remountToContainer(initMountns, initUserns, containerslice[0], containerslice[1], true); err != nil {
|
|
lxcfs_log.Errorf("remount lxcfs dir to container(%s) failed: %v", containerslice[0], err)
|
|
}
|
|
res <- struct{}{}
|
|
}()
|
|
select {
|
|
case <-res:
|
|
case <-time.After(30 * time.Second): // 30s timeout
|
|
lxcfs_log.Errorf("remount lxcfs dir to container(%s) timeout", containerslice[0])
|
|
}
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
lxcfs_log.Info(" remount All done...")
|
|
return nil
|
|
}
|
|
|
|
func remountToContainer(initMountns, initUserns, containerid string, pid string, isAll bool) error {
|
|
if isAll == false {
|
|
var err error
|
|
pid, err = isContainerExsit(containerid)
|
|
if err != nil {
|
|
onfail(err)
|
|
}
|
|
}
|
|
|
|
lxcfs_log.Infof("begin remount container,container id: %s, pid: %s", containerid, pid)
|
|
|
|
lxcfssubpath, err := ioutil.ReadDir("/var/lib/lxc/lxcfs/proc")
|
|
if err != nil {
|
|
return fmt.Errorf("Parse lxcfs dir failed: %v", err)
|
|
}
|
|
|
|
mountns, err := os.Readlink("/proc/" + pid + "/ns/mnt")
|
|
if err != nil {
|
|
return fmt.Errorf("read container mount namespace fail: %v", err)
|
|
}
|
|
if initMountns == mountns {
|
|
return fmt.Errorf("container pid changed: container mount namespace is same as init namespace")
|
|
}
|
|
|
|
var valuePaths []string
|
|
var valueMountPaths []string
|
|
for _, value := range lxcfssubpath {
|
|
valuePaths = append(valuePaths, fmt.Sprintf("/proc/%s", value.Name()))
|
|
valueMountPaths = append(valueMountPaths, fmt.Sprintf("/var/lib/lxc/lxcfs/proc/%s", value.Name()))
|
|
}
|
|
|
|
if err := libmount.NsExecUmount(pid, valuePaths); err != nil {
|
|
lxcfs_log.Errorf("unmount %v for container error: %v", valuePaths, err)
|
|
}
|
|
|
|
if err := libmount.NsExecMount(pid, "", valueMountPaths, valuePaths); err != nil {
|
|
lxcfs_log.Errorf("mount %v into container %s error: %v", valueMountPaths, containerid, err)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func isContainerExsit(containerid string) (string, error) {
|
|
lxcfs_log.Info("begin isContainerExsit...")
|
|
if containerid == "" {
|
|
return "", fmt.Errorf("Containerid mustn't be empty")
|
|
}
|
|
|
|
out, err := execCommond("isula", []string{"ps", "--format", "{{.ID}} {{.Pid}}"})
|
|
if err != nil {
|
|
onfail(err)
|
|
}
|
|
|
|
for _, value := range out {
|
|
containerslice := strings.Fields(value)
|
|
if len(containerslice) < 2 {
|
|
continue
|
|
}
|
|
if strings.Contains(containerslice[0], containerid) {
|
|
return containerslice[1], nil
|
|
}
|
|
}
|
|
|
|
return "", fmt.Errorf("No such Container %s in this node", containerid)
|
|
}
|
|
|
|
func execCommond(command string, params []string) ([]string, error) {
|
|
cmd := exec.Command(command, params...)
|
|
res := []string{
|
|
" ",
|
|
}
|
|
lxcfs_log.Info("exec cmd :", cmd.Args)
|
|
|
|
stdout, err := cmd.StdoutPipe()
|
|
|
|
if err != nil {
|
|
return res, err
|
|
}
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
return res, err
|
|
}
|
|
|
|
reader := bufio.NewReader(stdout)
|
|
|
|
for {
|
|
line, err2 := reader.ReadString('\n')
|
|
if err2 == io.EOF {
|
|
break
|
|
} else if err2 != nil {
|
|
onfail(err2)
|
|
}
|
|
res = append(res, line)
|
|
}
|
|
|
|
if err := cmd.Wait(); err != nil {
|
|
return res, err
|
|
}
|
|
|
|
return res, nil
|
|
}
|