github.com/kubeshop/testkube@v1.17.23/contrib/executor/kubepug/pkg/runner/runner.go (about) 1 package runner 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "os" 8 "strings" 9 10 kubepug "github.com/kubepug/kubepug/pkg/results" 11 12 "github.com/kubeshop/testkube/pkg/api/v1/testkube" 13 "github.com/kubeshop/testkube/pkg/envs" 14 "github.com/kubeshop/testkube/pkg/executor" 15 "github.com/kubeshop/testkube/pkg/executor/agent" 16 "github.com/kubeshop/testkube/pkg/executor/content" 17 "github.com/kubeshop/testkube/pkg/executor/env" 18 "github.com/kubeshop/testkube/pkg/executor/output" 19 "github.com/kubeshop/testkube/pkg/executor/runner" 20 "github.com/kubeshop/testkube/pkg/executor/scraper" 21 "github.com/kubeshop/testkube/pkg/executor/scraper/factory" 22 "github.com/kubeshop/testkube/pkg/ui" 23 ) 24 25 func NewRunner(ctx context.Context, params envs.Params) (*KubepugRunner, error) { 26 output.PrintLogf("%s Preparing test runner", ui.IconTruck) 27 28 var err error 29 r := &KubepugRunner{ 30 params: params, 31 } 32 33 r.Scraper, err = factory.TryGetScrapper(ctx, params) 34 if err != nil { 35 return nil, err 36 } 37 38 return r, nil 39 } 40 41 // KubepugRunner runs kubepug against cluster 42 type KubepugRunner struct { 43 params envs.Params 44 Scraper scraper.Scraper 45 } 46 47 var _ runner.Runner = &KubepugRunner{} 48 49 // Run runs the kubepug executable and parses it's output to be Testkube-compatible 50 func (r *KubepugRunner) Run(ctx context.Context, execution testkube.Execution) (testkube.ExecutionResult, error) { 51 if r.Scraper != nil { 52 defer r.Scraper.Close() 53 } 54 output.PrintLogf("%s Preparing for test run", ui.IconTruck) 55 56 path, workingDir, err := content.GetPathAndWorkingDir(execution.Content, r.params.DataDir) 57 if err != nil { 58 output.PrintLogf("%s Failed to resolve absolute directory for %s, using the path directly", ui.IconWarning, r.params.DataDir) 59 } 60 61 fileInfo, err := os.Stat(path) 62 if err != nil { 63 return testkube.ExecutionResult{}, err 64 } 65 66 if !fileInfo.IsDir() { 67 output.PrintLogf("%s Using single file: %v", ui.IconFile, execution) 68 } 69 70 if fileInfo.IsDir() { 71 output.PrintLogf("%s Using dir: %v", ui.IconFile, execution) 72 } 73 74 args, err := buildArgs(execution.Args, path) 75 if err != nil { 76 output.PrintLogf("%s Could not build up parameters: %s", ui.IconCross, err.Error()) 77 return testkube.ExecutionResult{}, fmt.Errorf("could not build up parameters: %w", err) 78 } 79 80 envManager := env.NewManagerWithVars(execution.Variables) 81 envManager.GetReferenceVars(envManager.Variables) 82 output.PrintLogf("%s Running kubepug with arguments: %v", ui.IconWorld, envManager.ObfuscateStringSlice(args)) 83 84 runPath := workingDir 85 command, args := executor.MergeCommandAndArgs(execution.Command, args) 86 output.PrintLogf("%s Test run command %s %s", ui.IconRocket, command, strings.Join(envManager.ObfuscateStringSlice(args), " ")) 87 out, err := executor.Run(runPath, command, envManager, args...) 88 out = envManager.ObfuscateSecrets(out) 89 if err != nil { 90 output.PrintLogf("%s Could not execute kubepug: %s", ui.IconCross, err.Error()) 91 return testkube.ExecutionResult{}, fmt.Errorf("could not execute kubepug: %w", err) 92 } 93 94 var rerr error 95 if execution.PostRunScript != "" && execution.ExecutePostRunScriptBeforeScraping { 96 output.PrintLog(fmt.Sprintf("%s Running post run script...", ui.IconCheckMark)) 97 98 if rerr = agent.RunScript(execution.PostRunScript, r.params.WorkingDir); rerr != nil { 99 output.PrintLogf("%s Failed to execute post run script %s", ui.IconWarning, rerr) 100 } 101 } 102 103 // scrape artifacts first even if there are errors above 104 if r.params.ScrapperEnabled && execution.ArtifactRequest != nil && len(execution.ArtifactRequest.Dirs) != 0 { 105 output.PrintLogf("Scraping directories: %v with masks: %v", execution.ArtifactRequest.Dirs, execution.ArtifactRequest.Masks) 106 107 if err := r.Scraper.Scrape(ctx, execution.ArtifactRequest.Dirs, execution.ArtifactRequest.Masks, execution); err != nil { 108 return testkube.ExecutionResult{}, fmt.Errorf("could not scrape kubepug directories: %w", err) 109 } 110 } 111 112 var kubepugResult kubepug.Result 113 err = json.Unmarshal(out, &kubepugResult) 114 if err != nil { 115 output.PrintLogf("%s could not unmarshal kubepug execution result: %s", ui.IconCross, err.Error()) 116 return testkube.ExecutionResult{}, fmt.Errorf("could not unmarshal kubepug execution result: %w", err) 117 } 118 119 if rerr != nil { 120 return testkube.ExecutionResult{}, rerr 121 } 122 123 deprecatedAPIstep := createDeprecatedAPIsStep(kubepugResult) 124 deletedAPIstep := createDeletedAPIsStep(kubepugResult) 125 return testkube.ExecutionResult{ 126 Status: getResultStatus(kubepugResult), 127 Output: string(out), 128 Steps: []testkube.ExecutionStepResult{ 129 deprecatedAPIstep, 130 deletedAPIstep, 131 }, 132 }, nil 133 } 134 135 // createDeprecatedAPIsStep checks the kubepug output for deprecated APIs and converts them to Testkube step result 136 func createDeprecatedAPIsStep(r kubepug.Result) testkube.ExecutionStepResult { 137 step := testkube.ExecutionStepResult{ 138 Name: "Deprecated APIs", 139 } 140 141 if len(r.DeprecatedAPIs) == 0 { 142 step.Status = "passed" 143 output.PrintLog(fmt.Sprintf("%s No deprecated APIs found", ui.IconCheckMark)) 144 return step 145 } 146 147 step.Status = "failed" 148 output.PrintLog(fmt.Sprintf("%s Found deprecated APIs: %v", ui.IconCross, r.DeletedAPIs)) 149 for _, api := range r.DeprecatedAPIs { 150 step.AssertionResults = append(step.AssertionResults, testkube.AssertionResult{ 151 Name: api.Kind, 152 Status: "failed", 153 ErrorMessage: fmt.Sprintf("Deprecated API:\n %v", api), 154 }) 155 } 156 157 return step 158 } 159 160 // createDeletedAPISstep checks the kubepug output for deleted APIs and converts them to Testkube step result 161 func createDeletedAPIsStep(r kubepug.Result) testkube.ExecutionStepResult { 162 step := testkube.ExecutionStepResult{ 163 Name: "Deleted APIs", 164 } 165 166 if len(r.DeletedAPIs) == 0 { 167 step.Status = "passed" 168 output.PrintLogf("%s No deleted APIs found", ui.IconCheckMark) 169 return step 170 } 171 172 step.Status = "failed" 173 output.PrintLogf("%s Found deleted APIs: %v", ui.IconCross, r.DeletedAPIs) 174 for _, api := range r.DeletedAPIs { 175 step.AssertionResults = append(step.AssertionResults, testkube.AssertionResult{ 176 Name: api.Kind, 177 Status: "failed", 178 ErrorMessage: fmt.Sprintf("Deleted API:\n %v", api), 179 }) 180 } 181 182 return step 183 } 184 185 // getResultStatus calculates the final result status 186 func getResultStatus(r kubepug.Result) *testkube.ExecutionStatus { 187 if len(r.DeletedAPIs) == 0 && len(r.DeprecatedAPIs) == 0 { 188 return testkube.ExecutionStatusPassed 189 } 190 return testkube.ExecutionStatusFailed 191 } 192 193 // buildArgs builds up the arguments for 194 func buildArgs(args []string, inputPath string) ([]string, error) { 195 for i := range args { 196 if args[i] == "<runPath>" { 197 args[i] = inputPath 198 } 199 200 args[i] = os.ExpandEnv(args[i]) 201 } 202 return args, nil 203 } 204 205 // GetType returns runner type 206 func (r *KubepugRunner) GetType() runner.Type { 207 return runner.TypeMain 208 }