232 lines
6.1 KiB
Go
232 lines
6.1 KiB
Go
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
// isulad-tools is licensed under the Mulan PSL v1.
|
|
// You can use this software according to the terms and conditions of the Mulan PSL v1.
|
|
// You may obtain a copy of Mulan PSL v1 at:
|
|
// http://license.coscl.org.cn/MulanPSL
|
|
// 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 v1 for more details.
|
|
// Description: isulad hook main function
|
|
// Author: zhangwei
|
|
// Create: 2018-01-18
|
|
|
|
// go base main package
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"os/signal"
|
|
"path/filepath"
|
|
"syscall"
|
|
|
|
"github.com/docker/docker/pkg/reexec"
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
_ "github.com/opencontainers/runc/libcontainer/nsenter"
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/sirupsen/logrus"
|
|
hconfig "isula.org/isulad-tools/config"
|
|
"isula.org/isulad-tools/container"
|
|
"isula.org/isulad-tools/utils"
|
|
)
|
|
|
|
var (
|
|
defaultHookConfigFile = "device_hook.json"
|
|
syslogTag = "hook "
|
|
bundleConfigFile = "ociconfig.json"
|
|
)
|
|
|
|
type hookData struct {
|
|
spec *specs.Spec
|
|
state *configs.HookState
|
|
hookConfig *hconfig.ContainerHookConfig
|
|
storagePath string
|
|
}
|
|
|
|
func setupLog(logfile string) {
|
|
logrus.SetLevel(logrus.DebugLevel)
|
|
logrus.SetOutput(os.Stdout)
|
|
if logfile != "" {
|
|
f, err := os.OpenFile(logfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0600)
|
|
if err != nil {
|
|
return
|
|
}
|
|
logrus.SetOutput(f)
|
|
return
|
|
}
|
|
fm := &logrus.TextFormatter{DisableTimestamp: true, DisableColors: true}
|
|
logrus.SetFormatter(fm)
|
|
if err := utils.HookSyslog("", syslogTag); err != nil {
|
|
fmt.Fprintf(os.Stdout, "%v", err)
|
|
}
|
|
}
|
|
|
|
func fatal(err error) {
|
|
if err != nil {
|
|
logrus.Error(err)
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func prepareHookData() (*hookData, error) {
|
|
var (
|
|
err error
|
|
spec *specs.Spec
|
|
compatSpec *specs.CompatSpec
|
|
state *configs.HookState
|
|
hookConfig *hconfig.ContainerHookConfig
|
|
containerStoragePath = ""
|
|
)
|
|
|
|
if state, err = utils.ParseHookState(os.Stdin); err != nil {
|
|
logrus.Errorf("Parse Hook State Failed: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
// Load container OCI spec from config
|
|
configFile := bundleConfigFile
|
|
if spec, err = utils.LoadSpec(filepath.Join(state.Bundle, configFile)); err != nil {
|
|
if compatSpec, err = utils.LoadCompatSpec(filepath.Join(state.Bundle, configFile)); err != nil {
|
|
logrus.Errorf("Failed to load spec for contianer %s: %v", state.ID, err)
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if compatSpec != nil {
|
|
capabilities := compatSpec.Process.Capabilities
|
|
spec = &compatSpec.Spec
|
|
spec.Process = compatSpec.Process.Process
|
|
spec.Process.Capabilities = &specs.LinuxCapabilities{
|
|
Bounding: capabilities,
|
|
Effective: capabilities,
|
|
Inheritable: capabilities,
|
|
Permitted: capabilities,
|
|
Ambient: capabilities,
|
|
}
|
|
}
|
|
|
|
if containerStoragePath, err = utils.GetContainerStoragePath(); err != nil {
|
|
logrus.Errorf("Failed to get container storage path: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
configPath := filepath.Join(containerStoragePath, state.ID, defaultHookConfigFile)
|
|
if _, err := os.Stat(configPath); err != nil {
|
|
// not an error, user do not add/remove device to/from this container.
|
|
return &hookData{
|
|
spec: spec,
|
|
state: state,
|
|
hookConfig: &hconfig.ContainerHookConfig{},
|
|
storagePath: containerStoragePath,
|
|
}, nil
|
|
}
|
|
|
|
// Load devices and binds config for container.
|
|
if hookConfig, err = hconfig.LoadContainerHookConfig(configPath); err != nil {
|
|
logrus.Errorf("Failed to parse Config File for container %s: %v", state.ID, err)
|
|
return nil, err
|
|
}
|
|
|
|
return &hookData{
|
|
spec: spec,
|
|
state: state,
|
|
hookConfig: hookConfig,
|
|
storagePath: containerStoragePath,
|
|
}, nil
|
|
}
|
|
|
|
func main() {
|
|
if reexec.Init() {
|
|
// `reexec routine` was registered in isulad-tools/libdevice
|
|
// Sub nsenter process will come here.
|
|
// Isulad reexec package do not handle errors.
|
|
// And sub device-hook nsenter init process will send back the error message to parenet through pipe.
|
|
// So here do not need to handle errors.
|
|
return
|
|
}
|
|
signal.Ignore(syscall.SIGINT, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM)
|
|
|
|
flLogfile := flag.String("log", "", "set output log file")
|
|
flMode := flag.String("state", "", "set isulad hook state mode: prestart or poststop")
|
|
// No requirements at present, by default don't enable this function.
|
|
flWithRelabel := flag.Bool("with-relabel", false, "isulad hook enable oci relabel hook function")
|
|
|
|
flag.Parse()
|
|
|
|
setupLog(*flLogfile)
|
|
hData, err := prepareHookData()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if err := os.MkdirAll(hconfig.IsuladToolsDir, 0666); err != nil {
|
|
logrus.Errorf("failed to set isulad-tools dir: %v", err)
|
|
}
|
|
|
|
switch *flMode {
|
|
case "prestart":
|
|
if hData.state.Pid <= 0 {
|
|
logrus.Errorf("can't get correct pid of container: %d", hData.state.Pid)
|
|
return
|
|
}
|
|
if err := prestartHook(hData, *flWithRelabel); err != nil {
|
|
fatal(err)
|
|
}
|
|
|
|
if err := updateHookData(hData); err != nil {
|
|
fatal(err)
|
|
}
|
|
case "poststart":
|
|
if hData.state.Pid <= 0 {
|
|
logrus.Errorf("can't get correct pid of container: %d", hData.state.Pid)
|
|
return
|
|
}
|
|
if err := poststartHook(hData, *flWithRelabel); err != nil {
|
|
fatal(err)
|
|
}
|
|
|
|
if err := updateHookData(hData); err != nil {
|
|
fatal(err)
|
|
}
|
|
|
|
case "poststop":
|
|
postStopHook(hData, *flWithRelabel)
|
|
}
|
|
}
|
|
|
|
func updateHookData(data *hookData) error {
|
|
|
|
var (
|
|
err error
|
|
containerStoragePath = ""
|
|
)
|
|
|
|
c := &container.Container{}
|
|
if data.storagePath != "" {
|
|
containerStoragePath = data.storagePath
|
|
} else {
|
|
containerStoragePath, err = utils.GetContainerStoragePath()
|
|
if err != nil {
|
|
logrus.Errorf("Failed to get container storage path: %v", err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
c.SetContainerPath(filepath.Join(containerStoragePath, data.state.ID))
|
|
|
|
if err := c.Lock(); err != nil {
|
|
return err
|
|
}
|
|
defer c.Unlock()
|
|
|
|
defer func() {
|
|
if err := data.hookConfig.Flush(); err != nil {
|
|
logrus.Infof("config Flush error:%v", err)
|
|
}
|
|
}()
|
|
return nil
|
|
}
|