Skip to content
Snippets Groups Projects
runner.go 5.27 KiB
Newer Older
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
package core

import (
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	"forge.tedomum.net/kludge/leviathan/core/dsl"
	"forge.tedomum.net/kludge/leviathan/libs"
	"forge.tedomum.net/kludge/leviathan/utils"
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	"github.com/panjf2000/ants"
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	"gopkg.in/yaml.v3"
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	"os"
	"path"
	"sync"
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
)

type Runner struct {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	Target    string
	Workspace string
	Workflow  libs.Workflow
	Options   *libs.Options
	Reports   []string
	DSL       map[string]interface{}

	Params map[string]string
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
}

func InitRunner(target string, options *libs.Options) (*Runner, error) {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	var runner Runner
	runner.Target = target
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	runner.Options = options

Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	// Create workspace if it does not exists
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	runner.Workspace = path.Join(options.Environment.Workspaces, utils.CleanPath(target))
	if _, err := os.Stat(runner.Workspace); os.IsNotExist(err) {
		utils.Logger.Debug().Str("workspace", runner.Workspace).Msg("Create workspace folder")
		if err = os.MkdirAll(runner.Workspace, 0700); err != nil {
			return &runner, err
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		}
	}

	// Init dsl with variables
	runner.initDSL()
	runner.initParams()

	// Create .tmp directory in workspace
	tmpDir := runner.Params["TempDir"]
	if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
		utils.Logger.Debug().Str("workspace", runner.Workspace).Msg("Create TMP folder")
		if err = os.MkdirAll(tmpDir, 0700); err != nil {
			return &runner, err
		}
	}

	// Create reports directory in workspace
	reportsDir := runner.Params["ReportsDir"]
	if _, err := os.Stat(reportsDir); os.IsNotExist(err) {
		utils.Logger.Debug().Str("workspace", runner.Workspace).Msg("Create reports folder")
		if err = os.MkdirAll(reportsDir, 0700); err != nil {
			return &runner, err
		}
	}

	// Add reports directory to whitelist
	runner.Reports = append(runner.Reports, runner.Params["ReportsDir"])

Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	// Try to import workflow
	if !runner.importWorkflow() {
		utils.Logger.Fatal().Str("workflow", runner.Options.Scan.Flow).Msg("Unable to import workflow")
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		return &runner, nil
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	}

Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	// Check modules
	for _, routine := range runner.Workflow.Routines {
		for _, module := range routine.Modules {
			if !utils.IsYamlValid(module, runner.Options.Environment.Modules) {
				utils.Logger.Fatal().Str("module", module).Msg("Module invalid")
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
				return &runner, nil
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
			}
		}
	}

Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	return &runner, nil
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
}

Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
func (r *Runner) Start() {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	utils.Logger.Info().
		Str("runner", r.Target).
		Str("workflow", r.Workflow.Name).
		Msg("Start runner")

	for _, routine := range r.Workflow.Routines {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		// Check for params in module
		for _, param := range routine.Params {
			for k, v := range param {
				// Routine params do not override workflow and user params
				if r.Params[k] == "" {
					r.setParam(k, v)
				}
			}
		}

		// Start each modules
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		var wg sync.WaitGroup
		p, _ := ants.NewPoolWithFunc(r.Options.Scan.Threads*10, func(m interface{}) {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
			module := m.(string)
			r.startModule(module)
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
			wg.Done()
		}, ants.WithPreAlloc(true))
		defer p.Release()

Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		for _, module := range routine.Modules {
			if err := p.Invoke(module); err != nil {
				utils.Logger.Error().Msg(err.Error())
			}
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
			wg.Add(1)
		}

		wg.Wait()
	}

	if !r.Options.Scan.NoClean {
		utils.Logger.Info().Str("workspace", r.Workspace).Msg("Clean workspace")
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		dsl.CleanWorkspace(r.Workspace, r.Reports)
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
}

Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
func (r *Runner) importWorkflow() bool {
	fullPath := utils.CheckExistence(r.Options.Scan.Flow, r.Options.Environment.Workflows)
	content, err := r.ParseTemplate(fullPath)
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	if err != nil {
		utils.Logger.Error().Msg(err.Error())
		return false
	}

	if err = yaml.Unmarshal(content, &r.Workflow); err != nil {
		utils.Logger.Error().Str("workflow", fullPath).Msg(err.Error())
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		return false
	}
	// Check validator
	if !r.ValidateInputType() {
		return false
	}

	// re-init params to handle Workflow params
	r.initParams()
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	return true
}

func (r *Runner) startModule(moduleName string) {
	utils.Logger.Info().Str("module", moduleName).Msg("Run module")

	// Import module before executing it
	m := libs.Module{}
	fullPath := utils.CheckExistence(moduleName, r.Options.Environment.Modules)
	content, _ := os.ReadFile(fullPath)
	if err := yaml.Unmarshal(content, &m); err != nil {
		utils.Logger.Error().Msg(err.Error())
		return
	}
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed

	// Check for params in module
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	for _, param := range m.Params {
		for k, v := range param {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
			// Module params do not override workflow, routine and user params
			if r.Params[k] == "" {
				r.setParam(k, v)
			}
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	// Save the reports generated by the module
	for _, report := range m.Reports {
		reportPath := r.ParseString(report)
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		utils.Logger.Debug().Str("module", m.Name).Str("report", reportPath).Msg("Add report to the list")
		r.Reports = append(r.Reports, utils.NormalizePath(reportPath))
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	// Check if module has to be skipped
	if r.Options.Scan.Resume && len(m.Reports) != 0 {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		skipModule := true
		for _, report := range m.Reports {
			if !utils.FileExists(r.ParseString(report)) {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
				skipModule = false
			}
		}
		if skipModule {
			utils.Logger.Info().Str("module", m.Name).Msg("skip module (--resume flag used)")
			return
		}
	}

	// Execute pre_run scripts
	if m.PreRun != nil {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		utils.Logger.Debug().Str("module", m.Name).Msg("Execute Pre_run scripts")
		r.runScripts(m.PreRun)
	}
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed

Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	utils.Logger.Debug().Str("module", m.Name).Msg("Execute step")
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	for _, step := range m.Steps {
		r.RunStep(&step)
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	}

	if m.PostRun != nil {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		utils.Logger.Debug().Str("module", m.Name).Msg("Execute post_run scripts")