github.com/kubeshop/testkube@v1.17.23/contrib/executor/init/pkg/runner/runner.go (about) 1 package runner 2 3 import ( 4 "context" 5 "os" 6 "path/filepath" 7 "strconv" 8 "strings" 9 10 "github.com/pkg/errors" 11 12 "github.com/kubeshop/testkube/pkg/api/v1/client" 13 "github.com/kubeshop/testkube/pkg/api/v1/testkube" 14 "github.com/kubeshop/testkube/pkg/envs" 15 "github.com/kubeshop/testkube/pkg/executor" 16 "github.com/kubeshop/testkube/pkg/executor/containerexecutor" 17 "github.com/kubeshop/testkube/pkg/executor/content" 18 "github.com/kubeshop/testkube/pkg/executor/output" 19 "github.com/kubeshop/testkube/pkg/executor/runner" 20 "github.com/kubeshop/testkube/pkg/storage/minio" 21 "github.com/kubeshop/testkube/pkg/ui" 22 ) 23 24 const ( 25 defaultShell = "/bin/sh" 26 preRunScriptName = "prerun.sh" 27 commandScriptName = "command.sh" 28 postRunScriptName = "postrun.sh" 29 ) 30 31 // NewRunner creates init runner 32 func NewRunner(params envs.Params) *InitRunner { 33 return &InitRunner{ 34 Fetcher: content.NewFetcher(params.DataDir), 35 Params: params, 36 } 37 } 38 39 // InitRunner prepares data for executor 40 type InitRunner struct { 41 Fetcher content.ContentFetcher 42 Params envs.Params 43 } 44 45 var _ runner.Runner = &InitRunner{} 46 47 // Run prepares data for executor 48 func (r *InitRunner) Run(ctx context.Context, execution testkube.Execution) (result testkube.ExecutionResult, err error) { 49 output.PrintLogf("%s Initializing...", ui.IconTruck) 50 51 gitUsername := r.Params.GitUsername 52 gitToken := r.Params.GitToken 53 54 if gitUsername != "" || gitToken != "" { 55 if execution.Content != nil && execution.Content.Repository != nil { 56 execution.Content.Repository.Username = gitUsername 57 execution.Content.Repository.Token = gitToken 58 } 59 } 60 61 if execution.VariablesFile != "" { 62 output.PrintLogf("%s Creating variables file...", ui.IconWorld) 63 file := filepath.Join(r.Params.DataDir, "params-file") 64 if err = os.WriteFile(file, []byte(execution.VariablesFile), 0666); err != nil { 65 output.PrintLogf("%s Could not create variables file %s: %s", ui.IconCross, file, err.Error()) 66 return result, errors.Errorf("could not create variables file %s: %v", file, err) 67 } 68 output.PrintLogf("%s Variables file created", ui.IconCheckMark) 69 } 70 71 _, err = r.Fetcher.Fetch(execution.Content) 72 if err != nil { 73 output.PrintLogf("%s Could not fetch test content: %s", ui.IconCross, err.Error()) 74 return result, errors.Errorf("could not fetch test content: %v", err) 75 } 76 77 if execution.PreRunScript != "" || execution.PostRunScript != "" { 78 shell := defaultShell 79 if execution.ContainerShell != "" { 80 shell = execution.ContainerShell 81 } 82 83 shebang := "#!" + shell + "\nset -e\n" 84 // No set -e so that we can run the post-run script even if the command fails 85 entrypoint := "#!" + shell + "\n" 86 command := shebang 87 preRunScript := shebang 88 postRunScript := shebang 89 90 if execution.PreRunScript != "" { 91 if execution.SourceScripts { 92 entrypoint += ". " 93 } 94 95 entrypoint += strconv.Quote(filepath.Join(r.Params.DataDir, preRunScriptName)) + "\n" 96 entrypoint += "prerun_exit_code=$?\nif [ $prerun_exit_code -ne 0 ]; then\n exit $prerun_exit_code\nfi\n" 97 preRunScript += execution.PreRunScript 98 } 99 100 if len(execution.Command) != 0 { 101 if execution.SourceScripts { 102 entrypoint += ". " 103 } 104 105 entrypoint += strconv.Quote(filepath.Join(r.Params.DataDir, commandScriptName)) + " $@\n" 106 entrypoint += "command_exit_code=$?\n" 107 command += strings.Join(execution.Command, " ") 108 command += " \"$@\"\n" 109 } 110 111 if execution.PostRunScript != "" { 112 if execution.SourceScripts { 113 entrypoint += ". " 114 } 115 116 entrypoint += strconv.Quote(filepath.Join(r.Params.DataDir, postRunScriptName)) + "\n" 117 entrypoint += "postrun_exit_code=$?\n" 118 postRunScript += execution.PostRunScript 119 } 120 121 if len(execution.Command) != 0 { 122 entrypoint += "if [ $command_exit_code -ne 0 ]; then\n exit $command_exit_code\nfi\n" 123 } 124 125 if execution.PostRunScript != "" { 126 entrypoint += "exit $postrun_exit_code\n" 127 } 128 var scripts = []struct { 129 dir string 130 file string 131 data string 132 comment string 133 }{ 134 {r.Params.DataDir, preRunScriptName, preRunScript, "prerun"}, 135 {r.Params.DataDir, commandScriptName, command, "command"}, 136 {r.Params.DataDir, postRunScriptName, postRunScript, "postrun"}, 137 {r.Params.DataDir, containerexecutor.EntrypointScriptName, entrypoint, "entrypoint"}, 138 } 139 140 for _, script := range scripts { 141 if script.data == "" { 142 continue 143 } 144 145 file := filepath.Join(script.dir, script.file) 146 output.PrintLogf("%s Creating %s script...", ui.IconWorld, script.comment) 147 if err = os.WriteFile(file, []byte(script.data), 0755); err != nil { 148 output.PrintLogf("%s Could not create %s script %s: %s", ui.IconCross, script.comment, file, err.Error()) 149 return result, errors.Errorf("could not create %s script %s: %v", script.comment, file, err) 150 } 151 output.PrintLogf("%s %s script created", ui.IconCheckMark, script.comment) 152 } 153 } 154 155 // TODO: write a proper cloud implementation 156 if r.Params.Endpoint != "" && !r.Params.ProMode { 157 output.PrintLogf("%s Fetching uploads from object store %s...", ui.IconFile, r.Params.Endpoint) 158 opts := minio.GetTLSOptions(r.Params.Ssl, r.Params.SkipVerify, r.Params.CertFile, r.Params.KeyFile, r.Params.CAFile) 159 minioClient := minio.NewClient(r.Params.Endpoint, r.Params.AccessKeyID, r.Params.SecretAccessKey, r.Params.Region, r.Params.Token, r.Params.Bucket, opts...) 160 fp := content.NewCopyFilesPlacer(minioClient) 161 fp.PlaceFiles(ctx, execution.TestName, execution.BucketName) 162 } else if r.Params.ProMode { 163 output.PrintLogf("%s Copy files functionality is currently not supported in cloud mode", ui.IconWarning) 164 } 165 166 output.PrintLogf("%s Setting up access to files in %s", ui.IconFile, r.Params.DataDir) 167 _, err = executor.Run(r.Params.DataDir, "chmod", nil, []string{"-R", "777", "."}...) 168 if err != nil { 169 output.PrintLogf("%s Could not chmod for data dir: %s", ui.IconCross, err.Error()) 170 } 171 172 if execution.ArtifactRequest != nil && 173 (execution.ArtifactRequest.StorageClassName != "" || execution.ArtifactRequest.UseDefaultStorageClassName) { 174 mountPath := filepath.Join(r.Params.DataDir, "artifacts") 175 if execution.ArtifactRequest.VolumeMountPath != "" { 176 mountPath = execution.ArtifactRequest.VolumeMountPath 177 } 178 179 _, err = executor.Run(mountPath, "chmod", nil, []string{"-R", "777", "."}...) 180 if err != nil { 181 output.PrintLogf("%s Could not chmod for artifacts dir: %s", ui.IconCross, err.Error()) 182 } 183 } 184 output.PrintLogf("%s Access to files enabled", ui.IconCheckMark) 185 186 if len(execution.DownloadArtifactExecutionIDs) != 0 || len(execution.DownloadArtifactTestNames) != 0 { 187 downloadedArtifacts := filepath.Join(r.Params.DataDir, "downloaded-artifacts") 188 options := client.Options{ 189 ApiUri: r.Params.APIURI, 190 } 191 192 c, err := client.GetClient(client.ClientDirect, options) 193 if err != nil { 194 output.PrintLogf("%s Could not get client: %s", ui.IconCross, err.Error()) 195 } else { 196 for _, id := range execution.DownloadArtifactExecutionIDs { 197 execution, err := c.GetExecution(id) 198 if err != nil { 199 output.PrintLogf("%s Could not get execution: %s", ui.IconCross, err.Error()) 200 continue 201 } 202 203 if err = downloadArtifacts(id, filepath.Join(downloadedArtifacts, execution.TestName+"-"+id), c); err != nil { 204 output.PrintLogf("%s Could not download execution artifact: %s", ui.IconCross, err.Error()) 205 } 206 } 207 208 for _, name := range execution.DownloadArtifactTestNames { 209 test, err := c.GetTestWithExecution(name) 210 if err != nil { 211 output.PrintLogf("%s Could not get test with execution: %s", ui.IconCross, err.Error()) 212 continue 213 } 214 215 if test.LatestExecution != nil { 216 id := test.LatestExecution.Id 217 if err = downloadArtifacts(id, filepath.Join(downloadedArtifacts, name+"-"+id), c); err != nil { 218 output.PrintLogf("%s Could not download test artifact: %s", ui.IconCross, err.Error()) 219 } 220 } 221 } 222 } 223 } 224 225 output.PrintLogf("%s Initialization successful", ui.IconCheckMark) 226 return testkube.NewPendingExecutionResult(), nil 227 } 228 229 func downloadArtifacts(id, dir string, c client.Client) error { 230 artifacts, err := c.GetExecutionArtifacts(id) 231 if err != nil { 232 return err 233 } 234 235 if err = os.MkdirAll(dir, os.ModePerm); err != nil { 236 return err 237 } 238 239 if len(artifacts) > 0 { 240 output.PrintLogf("%s Getting %d artifacts...", ui.IconWorld, len(artifacts)) 241 for _, artifact := range artifacts { 242 f, err := c.DownloadFile(id, artifact.Name, dir) 243 if err != nil { 244 return err 245 } 246 247 output.PrintLogf("%s Downloading file %s...", ui.IconWorld, f) 248 } 249 } 250 251 return nil 252 } 253 254 // GetType returns runner type 255 func (r *InitRunner) GetType() runner.Type { 256 return runner.TypeInit 257 }