小工具分享:脚本执行工具Executor

脚本执行工具Executor

日常工作中可能有一些按顺序执行脚本的需求, 出现错误时可能还需要回滚, 这时候就需要一个工具来帮助我们完成这些工作。 Executor就是这样一个工具。

下面和大家分享一下这个小工具的实现及使用。

工具项目地址: https://github.com/Forget-C/gopkg

工具目录:/pkg/scripts

代码实现

代码实现比较简单。

pkg|main ⇒ tree scripts 
scripts
├── README.md
├── checker.go
├── executor.go
├── executor_test.go
├── finder.go
└── finder_test.go

executor.go

executor.go文件中定义了脚本执行器相关的结构体和方法。

type Executor interface {
	// Run run script and return success, output, error
	// if out is nil, output will be return in []byte
	Run(ctx context.Context, out io.Writer) (bool, []byte, error)
	// MakeCMD make exec.Cmd
	// some executor don't need this method
	MakeCMD(ctx context.Context, out io.Writer) *exec.Cmd
	// ScriptType return script type
	ScriptType() ScriptType
}

MakeExecutor方法会根据文件后缀创建对应的脚本执行器,目前支持的脚本类型有:pythonsqlbash二进制可执行文件

func MakeExecutor(filePath string) Executor {
	ext := filepath.Ext(filePath)
	switch ext {
	case ".py":
		return NewPythonExecutor(filePath, nil)
	case ".sh", ".bash":
		return NewBashExecutor(filePath, nil)
	case ".sql":
		return NewSQLExecutor(filePath)
	case "", ".bin":
		return NewBinaryExecutor(filePath, nil)
	default:
		return nil
	}
}

ExecutorImpl实现了基础方法

func (e *ExecutorImpl) Run(ctx context.Context, out io.Writer) (bool, []byte, error) {
	consoleOut := true
	_out := out
	if _out == nil {
		consoleOut = false
		_out = &bytes.Buffer{}
	}
    // 命令执行的模式
	cmd := e.MakeCMD(ctx, _out)
	err := cmd.Run()
	var outRes []byte
	if !consoleOut {
		outRes = _out.(*bytes.Buffer).Bytes()
	}
	if err != nil {
		return false, outRes, err
	}
	return true, outRes, nil
}

SQLExecutor是单独实现的方法,因为他不是使用运行命令的方式执行脚本, 而是使用了go-sql-driver来执行脚本。

func (e *SQLExecutor) SetConfig(cfg *viper.Viper) {
    // 存储mysql连接配置信息
	e.Config = cfg
}
func (e *SQLExecutor) Run(ctx context.Context, out io.Writer) (bool, []byte, error) {
	returner := func(err error) (bool, []byte, error) {
		if out != nil {
			if err != nil {
				out.Write([]byte(err.Error()))
				return false, nil, err
			} else {
				fmt.Fprintf(out, "run script: %s success", e.ScriptPath)
				return true, nil, nil
			}
		} else {
			if err != nil {
				return false, []byte(err.Error()), err
			}
		}
		return true, nil, nil
	}
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		e.Config.GetString("MYSQL_USER"),
		e.Config.GetString("MYSQL_PASSWORD"),
		e.Config.GetString("MYSQL_HOST"),
		e.Config.GetInt("MYSQL_PORT"),
		e.Config.GetString("MYSQL_DATABASE"),
	)

	sqlScript, err := os.ReadFile(e.ScriptPath)
	if err != nil {
		return returner(err)
	}

	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return returner(err)
	}
	defer db.Close()

	_, err = db.ExecContext(ctx, string(sqlScript))
	if err != nil {
		return returner(err)
	}
	return returner(nil)
}

你可以实现自己的MakeExecutorExecutor来支持更多脚本类型

finder.go

finder.go文件中定义了脚本查找器和管理器相关的结构体和方法。

Scan

Scan方法会扫描指定目录下的所有脚本文件,并按照顺序返回脚本列表。

脚本的命名规则:

  • 以数字开头,数字越小,越早执行
  • "_"用于分割, 例如:001_init.sh -> 002_init.sh -> 003_init.sh
  • 以"rollback"结尾的为回滚脚本,用于正常脚本执行失败时的回滚操作,例如:001_init_rollback.sh
ScriptManager

ScriptManager提供脚本管理的相关方法,用于执行脚本。

ScriptManager 将脚本定义为三个阶段, prerunpost

Prepare

ScriptManager.checkers用于检查是否满足脚本执行条件以及初始化一些数据到stat中。

ScriptManager.getters用于从stat中获取指定数据

func NewScriptManager(baseDir string, logger *log.Logger, out io.Writer) *ScriptManager {
	return &ScriptManager{
		BaseDir:       baseDir,
		Logger:        logger,
		SkipE:         false,
		Out:           out,
		SkipRollbackE: true,
		scripts:       make(map[int]Scripts),
		State:         make(map[string]interface{}),
		getters: []PrepareFunc{
			MysqlPreGetter,
		},
		checkers: []PrepareFunc{
			MysqlPreChecker,
		},
	}
}

SQLExecutor中的参数初始化和参数获取就是通过这两个方法来实现的。 具体实现在checker.go中。

这两个方法需要显式调用ScriptManager.Prepare方法来执行。

Executor cli

cli程序目录: /cmd/executor/executor.go

executor用于脚本执行工作。

例如:初始化数据库,初始化配置文件等。

executor 将会执行指定目录中的所有脚本。

程序将会在--base-dir指定的目录下查找scripts目录,执行scripts目录下的所有脚本。

脚本的执行顺序为:pre_init目录下的脚本 -> init目录下的脚本 -> post-init目录下的脚本。

脚本名称

脚本的命名规则:

  • 以数字开头,数字越小,越早执行
  • "_"用于分割, 例如:001_init.sh -> 002_init.sh -> 003_init.sh
  • 以"rollback"结尾的为回滚脚本,用于正常脚本执行失败时的回滚操作,例如:001_init_rollback.sh

脚本执行顺序

正常执行的脚本将按照文件名前的数字顺序执行,例如:001-init.sh -> 002-init.sh -> 003-init.sh。

001-init.sh
002-init.sh
003-init.sh

回滚脚本是在正常执行的脚本执行失败时执行的, 将会在失败的序号开始,逆序执行rollback脚本:

如当前执行到 002_init.sh , 此脚本执行失败, 那么将会执行 002_init_rollback.sh -> 001_init_rollback.sh

002_init_rollback.sh
001_init_rollback.sh

支持的脚本类型

脚本类型是通过文件后缀判断的, 目前支持 python、sql、bash、二进制可执行文件

参数

当有SQL类型脚本需要设置环境变量提供连接信息:

  • MYSQL_HOST 数据库地址
  • MYSQL_PORT 数据库端口
  • MYSQL_USER 数据库用户名
  • MYSQL_PASSWORD 数据库密码
  • MYSQL_DATABASE 数据库名称

--help查看帮助

gopkg|master⚡ ⇒ ./executor --help                 
                                      _
   ___  __  __   ___    ___   _   _  | |_    ___    _ __
  / _ \ \ \/ /  / _ \  / __| | | | | | __|  / _ \  | '__|
 |  __/  >  <  |  __/ | (__  | |_| | | |_  | (_) | | |
  \___| /_/\_\  \___|  \___|  \__,_|  \__|  \___/  |_|
The executor is script run/management tools.
For example: initialize the database, initialize the configuration file, etc.
The program will find the scripts directory in the base-dir directory and execute all scripts in the scripts directory.
The script is executed in the following order: scripts in the pre_init directory-> scripts in the init directory-> scripts in the post-init directory.
The script will be executed according to the number sequence before the file name, for example: 001-init.sh -> 002-init.sh -> 003-init.sh.
If there is no number before the script file name, it will be executed in the dictionary order of the file name, for example: a-init.sh -> b-init.sh -> c-init.sh.
Any script execution fails and the program will exit.

Usage:
  executor [flags]

Flags:
      --base-dir string       The base directory for finding default files. (default "./scripts")
      --enable-post-scripts   Enable post-run scripts. (default true)
      --enable-pre-scripts    Enable pre-run scripts. (default true)
  -h, --help                  help for executor
      --skip-error            Skip errors when exec error.
      --skip-rollback-error   Skip errors when exec rollback error. (default true)

你可能感兴趣的:(杂记,linux,linux,bash,go,executor)