From c0c33502bda57161461fac9926ff7b8d387730f1 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Wed, 1 Nov 2023 18:21:52 +0300 Subject: [PATCH] Improve recipe validation --- cli/cli.go | 3 +- cli/executor/executor.go | 7 ++++- cli/executor/validators.go | 61 +++++++++++++++++++++++++++++++++++++- 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index a4f39c70..96f16c55 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -361,11 +361,12 @@ func getValidationConfig(tags []string) *executor.ValidationConfig { if options.GetB(OPT_DRY_RUN) { vc.IgnoreDependencies = true + vc.IgnorePackages = true vc.IgnorePrivileges = true } if options.GetB(OPT_IGNORE_PACKAGES) { - vc.IgnoreDependencies = true + vc.IgnorePackages = true } return vc diff --git a/cli/executor/executor.go b/cli/executor/executor.go index dd184009..99d4c3c1 100644 --- a/cli/executor/executor.go +++ b/cli/executor/executor.go @@ -64,6 +64,7 @@ type Config struct { // ValidationConfig is config for validation type ValidationConfig struct { Tags []string + IgnorePackages bool IgnoreDependencies bool IgnorePrivileges bool } @@ -171,10 +172,14 @@ func (e *Executor) Validate(r *recipe.Recipe, cfg *ValidationConfig) []error { errs.Add(checkRecipePrivileges(r)) } - if !cfg.IgnoreDependencies { + if !cfg.IgnorePackages { errs.Add(checkPackages(r)) } + if !cfg.IgnoreDependencies { + errs.Add(checkDependencies(r)) + } + if !errs.HasErrors() { return nil } diff --git a/cli/executor/validators.go b/cli/executor/validators.go index a4393ad9..dad8c610 100644 --- a/cli/executor/validators.go +++ b/cli/executor/validators.go @@ -121,7 +121,7 @@ func checkRecipeVariables(r *recipe.Recipe) []error { return errs } -// checkPackages checks packages +// checkPackages checks if required packages are installed on the system func checkPackages(r *recipe.Recipe) []error { if len(r.Packages) == 0 { return nil @@ -137,6 +137,65 @@ func checkPackages(r *recipe.Recipe) []error { return []error{errors.New("Can't check required packages availability: Unsupported OS")} } +// checkDependencies checks if all required binaries are present on the system +func checkDependencies(r *recipe.Recipe) []error { + var errs []error + + binCache := make(map[string]bool) + + for _, c := range r.Commands { + for _, a := range c.Actions { + var binary string + + switch a.Name { + case recipe.ACTION_SERVICE_PRESENT: + binary = "systemctl" + case recipe.ACTION_SERVICE_ENABLED: + binary = "systemctl" + case recipe.ACTION_SERVICE_WORKS: + binary = "systemctl" + case recipe.ACTION_WAIT_SERVICE: + binary = "systemctl" + case recipe.ACTION_LIB_LOADED: + binary = "ldconfig" + case recipe.ACTION_LIB_CONFIG: + binary = "pkg-config" + case recipe.ACTION_LIB_LINKED: + binary = "readelf" + case recipe.ACTION_LIB_RPATH: + binary = "readelf" + case recipe.ACTION_LIB_SONAME: + binary = "readelf" + case recipe.ACTION_LIB_EXPORTED: + binary = "nm" + case recipe.ACTION_PYTHON2_PACKAGE: + binary = "python" + case recipe.ACTION_PYTHON3_PACKAGE: + binary = "python3" + } + + if !hasBinary(binCache, binary) { + errs = append(errs, fmt.Errorf("Action %q requires %q binary")) + } + } + } + + return errs +} + +// hasBinary checks if binary is present on the system +func hasBinary(binCache map[string]bool, binary string) bool { + isExist, ok := binCache[binary] + + if ok { + return isExist + } + + binCache[binary] = env.Which(binary) != "" + + return binCache[binary] +} + // getDynamicVars returns slice with dynamic vars func getDynamicVars(a *recipe.Action) []string { switch a.Name {