main.go 4.9 KB
Newer Older
W
wangkang101 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
/*
 * Copyright (c) 2020 Huawei Technologies Co., Ltd.
 * isula-transform is licensed under 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.
 * Create: 2020-04-24
 */

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
	"gopkg.in/natefinch/lumberjack.v2"
	"isula.org/isula-transform/transform"
	_ "isula.org/isula-transform/transform/register"
	"isula.org/isula-transform/utils"
)

const (
	exitNormal = iota
	exitInitErr
	exitTransformErr

	maxConcurrentTransform = 128
	maxPerLogFileSize      = 10 // megabytes
)

var (
	version   string
	gitCommit string
)

func genVersion() string {
	versions := []string{
		"version:" + version,
		"commit:" + gitCommit,
	}
	return strings.Join(versions, "\t")
}

func main() {
	app := &cli.App{
		Name:      "isula-transform",
		Usage:     "transform specify docker container type configuration to iSulad type",
		UsageText: "[global options] --all|container_id[ container_id...]",
		Version:   genVersion(),
		Action:    start,
	}
	for _, v := range transformFlags {
		app.Flags = append(app.Flags, v...)
	}
	if err := app.Run(os.Args); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(exitInitErr)
	}
	os.Exit(exitNormal)
}

func start(ctx *cli.Context) error {
	logInit(ctx)
	if err := transformInit(ctx); err != nil {
		return cli.NewExitError(err.Error(), exitInitErr)
	}
	return doTransform(ctx)
}

func logInit(ctx *cli.Context) {
	logPath := ctx.GlobalString("log")
	logRoot := filepath.Dir(logPath)
	if err := os.MkdirAll(logRoot, 0660); err != nil {
		logrus.SetOutput(os.Stdout)
		logrus.Infof("create the log directory %s failed: %v, using STDOUT", logRoot, err)
	} else {
		logrus.SetOutput(&lumberjack.Logger{
			Filename: logPath,
			MaxSize:  maxPerLogFileSize,
			Compress: true,
		})
	}

	logrus.SetLevel(transLogLevel(ctx.GlobalString("log-level")))
}

func transLogLevel(lvl string) logrus.Level {
	switch strings.ToLower(lvl) {
	case "error":
		return logrus.ErrorLevel
	case "warn":
		return logrus.WarnLevel
	case "debug":
		return logrus.DebugLevel
	default:
	}
	return logrus.InfoLevel
}

func transformInit(ctx *cli.Context) error {
	var iSuladCfg = struct {
		Graph         string `json:"graph"`
		State         string `json:"state"`
		Runtime       string `json:"default-runtime"`
		LogLevel      string `json:"log-level"`
		LogDriver     string `json:"log-driver"`
		StorageDriver string `json:"storage-driver"`
		ImageServer   string `json:"image-server-sock-addr"`
	}{}
	iSuladCfgFile := ctx.GlobalString("isulad-config-file")
	if err := utils.CheckFileValid(iSuladCfgFile); err != nil {
		return errors.Wrapf(err, "check isulad daemon config failed")
	}
	iSuladCfgData, err := ioutil.ReadFile(iSuladCfgFile)
	if err != nil {
		logrus.Errorf("read isulad daemon config failed: %v, file path: %s", err, iSuladCfgFile)
		return errors.Wrapf(err, "read isulad daemon config failed")
	}
	err = json.Unmarshal(iSuladCfgData, &iSuladCfg)
	if err != nil {
		logrus.Errorf("unmarshal isulad daemon config failed: %v, file path: %s", err, iSuladCfgFile)
		return errors.Wrapf(err, "unmarshal isulad daemon config failed")
	}

	logrus.Debugf("isulad daemon config: %+v", iSuladCfg)
	err = transform.InitIsuladTool(iSuladCfg.Graph, iSuladCfg.Runtime, iSuladCfg.StorageDriver, iSuladCfg.ImageServer)
	if err != nil {
		return errors.Wrapf(err, "transform init failed")
	}
	if iSuladCfg.LogDriver != "file" {
		logrus.Infof("isula daemon log driver is %s, can't redirect to file", iSuladCfg.LogDriver)
	} else {
		transform.LcrLogInit(iSuladCfg.State, iSuladCfg.Runtime, iSuladCfg.LogLevel)
	}
	return nil
}

func doTransform(ctx *cli.Context) error {
	e := transform.GetTransformer(ctx)
	if e == nil {
		return cli.NewExitError("get transform engine failed", exitInitErr)
	}
	if err := e.Init(); err != nil {
		return cli.NewExitError("transform engine init failed", exitInitErr)
	}

	var ids []string
	all := ctx.GlobalBool("all")
	if !all {
		if ctx.Args().Present() {
			ids = append(ctx.Args().Tail(), ctx.Args().First())
		} else {
			exitMsg := "isula-transform requires at least one container id as an input or setting the --all flag"
			return cli.NewExitError(exitMsg, exitInitErr)
		}
	}

	exitCode := exitNormal
	retCh := make(chan transform.Result, maxConcurrentTransform)
	go e.Transform(ids, all, retCh)
	for ret := range retCh {
		if !ret.Ok {
			exitCode = exitTransformErr
			fmt.Fprintln(os.Stderr, ret.Msg)
		} else {
			fmt.Fprintln(os.Stdout, ret.Msg)
		}
	}
	if exitCode != exitNormal {
		return cli.NewExitError("The transformation has been completed, but at least one failed", exitTransformErr)
	}
	return nil
}