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:
parent
798242c5bd
commit
4d3cb00b76
2 changed files with 60 additions and 39 deletions
|
@ -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()
|
||||
}
|
||||
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()
|
||||
}
|
||||
result.Print()
|
||||
return err
|
||||
}
|
||||
|
|
5
main.go
5
main.go
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue