1
0
Fork 0
mirror of https://github.com/kastenhq/kubestr.git synced 2024-12-14 11:57:56 +00:00

Separate JSON output, emit non-zero exit code (#82)

* Allow for printing FIO and CSICheck JSON results to file

Fixes #79

* Use the file output function in Baseline too, remove double printing

* Emit non-zero exit code when error has occurred

Fixes #80

* Don't output twice in error case

* Added godoc and a note in command help text

* Eliminate weird variable names, make godoc clearer

* Error handling for file output
This commit is contained in:
Markus Vuorio 2021-10-07 20:55:17 +03:00 committed by GitHub
parent 798242c5bd
commit 4d3cb00b76
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 39 deletions

View file

@ -18,6 +18,7 @@ import (
"context"
"encoding/json"
"fmt"
"os"
"time"
"github.com/kastenhq/kubestr/pkg/csi"
@ -29,16 +30,18 @@ import (
var (
output string
outfile string
rootCmd = &cobra.Command{
Use: "kubestr",
Short: "A tool to validate kubernetes storage",
Long: `kubestr is a tool that will scan your k8s cluster
and validate that the storage systems in place as well as run
performance tests.`,
Run: func(cmd *cobra.Command, args []string) {
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
Baseline(ctx, output)
return Baseline(ctx, output)
},
}
@ -53,10 +56,10 @@ var (
Use: "fio",
Short: "Runs an fio test",
Long: `Run an fio test`,
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
Fio(ctx, output, storageClass, fioCheckerSize, namespace, fioCheckerTestName, fioCheckerFilePath, containerImage)
return Fio(ctx, output, outfile, storageClass, fioCheckerSize, namespace, fioCheckerTestName, fioCheckerFilePath, containerImage)
},
}
@ -68,21 +71,22 @@ var (
Use: "csicheck",
Short: "Runs the CSI snapshot restore check",
Long: "Validates a CSI provisioners ability to take a snapshot of an application and restore it",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
CSICheck(ctx, output, namespace, storageClass, csiCheckVolumeSnapshotClass, csiCheckRunAsUser, containerImage, csiCheckCleanup, csiCheckSkipCFSCheck)
return CSICheck(ctx, output, outfile, namespace, storageClass, csiCheckVolumeSnapshotClass, csiCheckRunAsUser, containerImage, csiCheckCleanup, csiCheckSkipCFSCheck)
},
}
)
func init() {
rootCmd.PersistentFlags().StringVarP(&output, "output", "o", "", "Options(json)")
rootCmd.PersistentFlags().StringVarP(&outfile, "outfile", "e", "", "The file where test results will be written")
rootCmd.AddCommand(fioCmd)
fioCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a Storageclass. (Required)")
_ = fioCmd.MarkFlagRequired("storageclass")
fioCmd.Flags().StringVarP(&fioCheckerSize, "size", "z", fio.DefaultPVCSize, "The size of the volume used to run FIO.")
fioCmd.Flags().StringVarP(&fioCheckerSize, "size", "z", fio.DefaultPVCSize, "The size of the volume used to run FIO. Note that the FIO job definition is not scaled accordingly.")
fioCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace used to run FIO.")
fioCmd.Flags().StringVarP(&fioCheckerFilePath, "fiofile", "f", "", "The path to a an fio config file.")
fioCmd.Flags().StringVarP(&fioCheckerTestName, "testname", "t", "", "The Name of a predefined kubestr fio test. Options(default-fio)")
@ -106,19 +110,19 @@ func Execute() error {
}
// Baseline executes the baseline check
func Baseline(ctx context.Context, output string) {
func Baseline(ctx context.Context, output string) error {
p, err := kubestr.NewKubestr()
if err != nil {
fmt.Println(err.Error())
return
return err
}
fmt.Print(kubestr.Logo)
result := p.KubernetesChecks()
if output == "json" {
jsonRes, _ := json.MarshalIndent(result, "", " ")
fmt.Println(string(jsonRes))
return
if PrintAndJsonOutput(result, output, outfile) {
return err
}
for _, retval := range result {
retval.Print()
fmt.Println()
@ -128,13 +132,9 @@ func Baseline(ctx context.Context, output string) {
provisionerList, err := p.ValidateProvisioners(ctx)
if err != nil {
fmt.Println(err.Error())
return
}
if output == "json" {
jsonRes, _ := json.MarshalIndent(result, "", " ")
fmt.Println(string(jsonRes))
return
return err
}
fmt.Println("Available Storage Provisioners:")
fmt.Println()
time.Sleep(500 * time.Millisecond) // Added to introduce lag.
@ -143,42 +143,61 @@ func Baseline(ctx context.Context, output string) {
fmt.Println()
time.Sleep(500 * time.Millisecond)
}
return err
}
// PrintAndJsonOutput Print JSON output to stdout and to file if arguments say so
// Returns whether we have generated output or JSON
func PrintAndJsonOutput(result []*kubestr.TestOutput, output string, outfile string) bool {
if output == "json" {
jsonRes, _ := json.MarshalIndent(result, "", " ")
if len(outfile) > 0 {
err := os.WriteFile(outfile, jsonRes, 0666)
if err != nil {
fmt.Println("Error writing output:", err.Error())
os.Exit(2)
}
} else {
fmt.Println(string(jsonRes))
}
return true
}
return false
}
// Fio executes the FIO test.
func Fio(ctx context.Context, output, storageclass, size, namespace, jobName, fioFilePath string, containerImage string) {
func Fio(ctx context.Context, output, outfile, storageclass, size, namespace, jobName, fioFilePath string, containerImage string) error {
cli, err := kubestr.LoadKubeCli()
if err != nil {
fmt.Println(err.Error())
return
return err
}
fioRunner := &fio.FIOrunner{
Cli: cli,
}
testName := "FIO test results"
var result *kubestr.TestOutput
if fioResult, err := fioRunner.RunFio(ctx, &fio.RunFIOArgs{
fioResult, err := fioRunner.RunFio(ctx, &fio.RunFIOArgs{
StorageClass: storageclass,
Size: size,
Namespace: namespace,
FIOJobName: jobName,
FIOJobFilepath: fioFilePath,
Image: containerImage,
}); err != nil {
})
if err != nil {
result = kubestr.MakeTestOutput(testName, kubestr.StatusError, err.Error(), fioResult)
} else {
result = kubestr.MakeTestOutput(testName, kubestr.StatusOK, fmt.Sprintf("\n%s", fioResult.Result.Print()), fioResult)
}
if output == "json" {
jsonRes, _ := json.MarshalIndent(result, "", " ")
fmt.Println(string(jsonRes))
return
}
var wrappedResult = []*kubestr.TestOutput{result}
if !PrintAndJsonOutput(wrappedResult, output, outfile) {
result.Print()
}
return err
}
func CSICheck(ctx context.Context, output,
func CSICheck(ctx context.Context, output, outfile,
namespace string,
storageclass string,
volumesnapshotclass string,
@ -186,17 +205,17 @@ func CSICheck(ctx context.Context, output,
containerImage string,
cleanup bool,
skipCFScheck bool,
) {
) error {
testName := "CSI checker test"
kubecli, err := kubestr.LoadKubeCli()
if err != nil {
fmt.Printf("Failed to load kubeCLi (%s)", err.Error())
return
return err
}
dyncli, err := kubestr.LoadDynCli()
if err != nil {
fmt.Printf("Failed to load kubeCLi (%s)", err.Error())
return
return err
}
csiCheckRunner := &csi.SnapshotRestoreRunner{
KubeCli: kubecli,
@ -218,10 +237,9 @@ func CSICheck(ctx context.Context, output,
result = kubestr.MakeTestOutput(testName, kubestr.StatusOK, "CSI application successfully snapshotted and restored.", csiCheckResult)
}
if output == "json" {
jsonRes, _ := json.MarshalIndent(result, "", " ")
fmt.Println(string(jsonRes))
return
}
var wrappedResult = []*kubestr.TestOutput{result}
if !PrintAndJsonOutput(wrappedResult, output, outfile) {
result.Print()
}
return err
}

View file

@ -18,10 +18,13 @@ package main
import (
"github.com/kastenhq/kubestr/cmd"
"os"
)
func main() {
_ = Execute()
if err := Execute(); err != nil {
os.Exit(1)
}
}
// Execute executes the main command