forked from toolshed/abra
@ -2,101 +2,69 @@ package recipe
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"coopcloud.tech/abra/cli/formatter"
|
||||
"coopcloud.tech/abra/cli/internal"
|
||||
"coopcloud.tech/abra/pkg/autocomplete"
|
||||
"coopcloud.tech/abra/pkg/config"
|
||||
"coopcloud.tech/tagcmp"
|
||||
"github.com/docker/distribution/reference"
|
||||
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var recipeLintCommand = &cli.Command{
|
||||
Name: "lint",
|
||||
Usage: "Lint a recipe",
|
||||
Aliases: []string{"l"},
|
||||
ArgsUsage: "<recipe>",
|
||||
Name: "lint",
|
||||
Usage: "Lint a recipe",
|
||||
Aliases: []string{"l"},
|
||||
ArgsUsage: "<recipe>",
|
||||
Flags: []cli.Flag{internal.OnlyErrorFlag},
|
||||
BashComplete: autocomplete.RecipeNameComplete,
|
||||
Action: func(c *cli.Context) error {
|
||||
recipe := internal.ValidateRecipe(c)
|
||||
|
||||
expectedVersion := false
|
||||
if recipe.Config.Version == "3.8" {
|
||||
expectedVersion = true
|
||||
}
|
||||
tableCol := []string{"ref", "rule", "satisfied", "severity", "resolve"}
|
||||
table := formatter.CreateTable(tableCol)
|
||||
|
||||
envSampleProvided := false
|
||||
envSample := fmt.Sprintf("%s/%s/.env.sample", config.RECIPES_DIR, recipe.Name)
|
||||
if _, err := os.Stat(envSample); !os.IsNotExist(err) {
|
||||
envSampleProvided = true
|
||||
} else if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
hasError := false
|
||||
bar := formatter.CreateProgressbar(-1, "running recipe lint rules...")
|
||||
for level := range recipePkg.LintRules {
|
||||
for _, rule := range recipePkg.LintRules[level] {
|
||||
ok, err := rule.Function(recipe)
|
||||
if err != nil {
|
||||
logrus.Warn(err)
|
||||
}
|
||||
|
||||
serviceNamedApp := false
|
||||
traefikEnabled := false
|
||||
healthChecksForAllServices := true
|
||||
allImagesTagged := true
|
||||
noUnstableTags := true
|
||||
semverLikeTags := true
|
||||
for _, service := range recipe.Config.Services {
|
||||
if service.Name == "app" {
|
||||
serviceNamedApp = true
|
||||
}
|
||||
if !ok && rule.Level == "error" {
|
||||
hasError = true
|
||||
}
|
||||
|
||||
for label := range service.Deploy.Labels {
|
||||
if label == "traefik.enable" {
|
||||
if service.Deploy.Labels[label] == "true" {
|
||||
traefikEnabled = true
|
||||
var result string
|
||||
if ok {
|
||||
result = "yes"
|
||||
} else {
|
||||
result = "NO"
|
||||
}
|
||||
|
||||
if internal.OnlyErrors {
|
||||
if !ok && rule.Level == "error" {
|
||||
table.Append([]string{rule.Ref, rule.Description, result, rule.Level, rule.HowToResolve})
|
||||
bar.Add(1)
|
||||
}
|
||||
} else {
|
||||
table.Append([]string{rule.Ref, rule.Description, result, rule.Level, rule.HowToResolve})
|
||||
bar.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
img, err := reference.ParseNormalizedNamed(service.Image)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
if reference.IsNameOnly(img) {
|
||||
allImagesTagged = false
|
||||
}
|
||||
|
||||
var tag string
|
||||
switch img.(type) {
|
||||
case reference.NamedTagged:
|
||||
tag = img.(reference.NamedTagged).Tag()
|
||||
case reference.Named:
|
||||
noUnstableTags = false
|
||||
}
|
||||
|
||||
if tag == "latest" {
|
||||
noUnstableTags = false
|
||||
}
|
||||
|
||||
if !tagcmp.IsParsable(tag) {
|
||||
semverLikeTags = false
|
||||
}
|
||||
|
||||
if service.HealthCheck == nil {
|
||||
healthChecksForAllServices = false
|
||||
}
|
||||
}
|
||||
|
||||
tableCol := []string{"rule", "satisfied"}
|
||||
table := formatter.CreateTable(tableCol)
|
||||
table.Append([]string{"compose files have the expected version", strconv.FormatBool(expectedVersion)})
|
||||
table.Append([]string{"environment configuration is provided", strconv.FormatBool(envSampleProvided)})
|
||||
table.Append([]string{"recipe contains a service named 'app'", strconv.FormatBool(serviceNamedApp)})
|
||||
table.Append([]string{"traefik routing enabled on at least one service", strconv.FormatBool(traefikEnabled)})
|
||||
table.Append([]string{"all services have a healthcheck enabled", strconv.FormatBool(healthChecksForAllServices)})
|
||||
table.Append([]string{"all images are using a tag", strconv.FormatBool(allImagesTagged)})
|
||||
table.Append([]string{"no usage of unstable 'latest' tags", strconv.FormatBool(noUnstableTags)})
|
||||
table.Append([]string{"all tags are using a semver-like format", strconv.FormatBool(semverLikeTags)})
|
||||
table.Render()
|
||||
if table.NumLines() > 0 {
|
||||
fmt.Println()
|
||||
table.Render()
|
||||
}
|
||||
|
||||
if hasError {
|
||||
logrus.Warn("watch out, some critical errors are present in your recipe config")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
BashComplete: autocomplete.RecipeNameComplete,
|
||||
}
|
||||
|
Reference in New Issue
Block a user