github.com/kubeshop/testkube@v1.17.23/cmd/kubectl-testkube/commands/crds/tests_crds.go (about) 1 package crds 2 3 import ( 4 "fmt" 5 "io/fs" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/spf13/cobra" 11 12 "github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common" 13 "github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common/validator" 14 "github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/tests" 15 "github.com/kubeshop/testkube/contrib/executor/postman/pkg/postman" 16 "github.com/kubeshop/testkube/pkg/api/v1/client" 17 "github.com/kubeshop/testkube/pkg/api/v1/testkube" 18 "github.com/kubeshop/testkube/pkg/crd" 19 "github.com/kubeshop/testkube/pkg/ui" 20 "github.com/kubeshop/testkube/pkg/utils" 21 ) 22 23 var ( 24 uri string 25 flags tests.CreateCommonFlags 26 ) 27 28 // NewCRDTestsCmd is command to generate test CRDs 29 func NewCRDTestsCmd() *cobra.Command { 30 cmd := &cobra.Command{ 31 Use: "tests-crds <manifestDirectory>", 32 Aliases: []string{"test-crd", "test-crds", "tests-crd"}, 33 Short: "Generate tests CRD file based on directory", 34 Long: `Generate tests manifest based on directory (e.g. for ArgoCD sync based on tests files)`, 35 Args: validator.ManifestsDirectory, 36 Run: func(cmd *cobra.Command, args []string) { 37 var ( 38 testContentType, file, name string 39 crdOnly bool 40 ) 41 cmd.Flags().StringVar(&testContentType, "test-content-type", "string", "") 42 cmd.Flags().BoolVar(&crdOnly, "crd-only", true, "generate only CRD") 43 cmd.Flags().StringVar(&uri, "uri", "", "") 44 cmd.Flags().StringVar(&file, "file", "", "") 45 cmd.Flags().StringVar(&name, "name", "", "") 46 if flags.ExecutorType == postman.PostmanCollectionType { 47 processPostmanFiles(cmd, args) 48 } else { 49 dir := args[0] 50 err := filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error { 51 if err != nil { 52 return nil 53 } 54 55 if info.IsDir() { 56 return nil 57 } 58 cmd.Flags().Set("file", path) 59 cmd.Flags().Set("name", utils.SanitizeName(filepath.Base(path))) 60 options, err := tests.NewUpsertTestOptionsFromFlags(cmd) 61 if err != nil { 62 fmt.Println("# getting test options for file", path, err.Error()) 63 fmt.Println("---") 64 return nil 65 } 66 (*testkube.TestUpsertRequest)(&options).QuoteTestTextFields() 67 data, err := crd.ExecuteTemplate(crd.TemplateTest, options) 68 if err != nil { 69 fmt.Println("# executing crd template for file", err.Error()) 70 fmt.Println("---") 71 return nil 72 } 73 74 fmt.Println(data) 75 fmt.Println("---") 76 return nil 77 }) 78 79 ui.ExitOnError("getting directory content", err) 80 } 81 }, 82 } 83 84 tests.AddCreateFlags(cmd, &flags) 85 return cmd 86 } 87 88 // ErrTypeNotDetected is not detcted test type error 89 var ErrTypeNotDetected = fmt.Errorf("type not detected") 90 91 // processPostmanFiles processes postman files 92 func processPostmanFiles(cmd *cobra.Command, args []string) error { 93 detector := postman.Detector{} 94 dir := args[0] 95 96 detectedTests := make(map[string]client.UpsertTestOptions, 0) 97 testEnvs := make(map[string]map[string]string, 0) 98 testSecretEnvs := make(map[string]map[string]string, 0) 99 100 var preRunScript, postRunScript []byte 101 102 err := filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error { 103 if err != nil { 104 return nil 105 } 106 107 if info.IsDir() { 108 return nil 109 } 110 111 if testName, envName, ok := detector.IsEnvName(path); ok { 112 113 if _, ok := testEnvs[envName]; !ok { 114 testEnvs[envName] = make(map[string]string, 0) 115 } 116 117 testEnvs[envName][testName] = path 118 return nil 119 } 120 121 if secretTestName, secretEnvName, ok := detector.IsSecretEnvName(path); ok { 122 123 if _, ok := testSecretEnvs[secretEnvName]; !ok { 124 testSecretEnvs[secretEnvName] = make(map[string]string, 0) 125 } 126 127 testSecretEnvs[secretEnvName][secretTestName] = path 128 return nil 129 } 130 131 cmd.Flags().Set("file", path) 132 test, err := tests.NewUpsertTestOptionsFromFlags(cmd) 133 if err != nil { 134 ui.Warn(fmt.Sprintf("generate test for file %s got an error: %v", path, err)) 135 ui.UseStderr() 136 return err 137 } 138 139 test.Name = utils.SanitizeName(filepath.Base(path)) 140 141 testName, ok := detector.IsTestName(path) 142 if !ok { 143 testName = test.Name 144 } 145 146 preRunScriptBody := string(preRunScript) 147 if preRunScriptBody != "" { 148 preRunScriptBody = fmt.Sprintf("%q", strings.TrimSpace(preRunScriptBody)) 149 } 150 151 postRunScriptBody := string(postRunScript) 152 if postRunScriptBody != "" { 153 postRunScriptBody = fmt.Sprintf("%q", strings.TrimSpace(postRunScriptBody)) 154 } 155 156 for key, value := range flags.Envs { 157 if value != "" { 158 flags.Envs[key] = fmt.Sprintf("%q", value) 159 } 160 } 161 162 vars, err := common.CreateVariables(cmd, true) 163 if err != nil { 164 return err 165 } 166 167 for name, variable := range vars { 168 if variable.Value != "" { 169 variable.Value = fmt.Sprintf("%q", variable.Value) 170 vars[name] = variable 171 } 172 } 173 174 test.ExecutionRequest = &testkube.ExecutionRequest{ 175 Command: flags.Command, 176 Args: flags.ExecutorArgs, 177 ArgsMode: flags.ArgsMode, 178 Envs: flags.Envs, 179 Variables: vars, 180 PreRunScript: preRunScriptBody, 181 PostRunScript: postRunScriptBody, 182 } 183 detectedTests[testName] = test 184 return nil 185 }) 186 187 ui.ExitOnError("getting directory content", err) 188 189 generateCRDs(addEnvToTests(detectedTests, testEnvs, testSecretEnvs)) 190 return nil 191 192 } 193 194 // addEnvToTest adds env files to tests 195 func addEnvToTests(tests map[string]client.UpsertTestOptions, 196 testEnvs, testSecretEnvs map[string]map[string]string) (envTests []client.UpsertTestOptions) { 197 detector := postman.Detector{} 198 for testName, test := range tests { 199 testMap := map[string]client.UpsertTestOptions{} 200 for envName := range testEnvs { 201 if filename, ok := testEnvs[envName][testName]; ok { 202 data, err := os.ReadFile(filename) 203 if err != nil { 204 ui.UseStderr() 205 ui.Warn(fmt.Sprintf("read variables file %s got an error: %v", filename, err)) 206 continue 207 } 208 209 envTest := test 210 envTest.Name = utils.SanitizeName(envTest.Name + "-" + envName) 211 if test.ExecutionRequest != nil { 212 envTest.ExecutionRequest = &testkube.ExecutionRequest{} 213 *envTest.ExecutionRequest = *test.ExecutionRequest 214 } 215 216 if envTest.ExecutionRequest == nil { 217 envTest.ExecutionRequest = &testkube.ExecutionRequest{} 218 } 219 220 envTest.ExecutionRequest.VariablesFile = fmt.Sprintf("%q", strings.TrimSpace(string(data))) 221 testMap[envTest.Name] = envTest 222 } 223 } 224 225 for secretEnvName := range testSecretEnvs { 226 if filename, ok := testSecretEnvs[secretEnvName][testName]; ok { 227 data, err := os.ReadFile(filename) 228 if err != nil { 229 ui.UseStderr() 230 ui.Warn(fmt.Sprintf("read secret variables file %s got an error: %v", filename, err)) 231 continue 232 } 233 234 variables, err := detector.GetSecretVariables(string(data)) 235 if err != nil { 236 ui.UseStderr() 237 ui.Warn(fmt.Sprintf("parse secret file %s got an error: %v", filename, err)) 238 continue 239 } 240 241 secretEnvTest := test 242 secretEnvTest.Name = utils.SanitizeName(secretEnvTest.Name + "-" + secretEnvName) 243 if test.ExecutionRequest != nil { 244 secretEnvTest.ExecutionRequest = &testkube.ExecutionRequest{} 245 *secretEnvTest.ExecutionRequest = *test.ExecutionRequest 246 } 247 248 if envTest, ok := testMap[secretEnvTest.Name]; ok { 249 secretEnvTest = envTest 250 } 251 252 if secretEnvTest.ExecutionRequest == nil { 253 secretEnvTest.ExecutionRequest = &testkube.ExecutionRequest{} 254 } 255 256 for key, value := range variables { 257 if value.Value != "" { 258 value.Value = fmt.Sprintf("%q", value.Value) 259 variables[key] = value 260 } 261 } 262 263 secretEnvTest.ExecutionRequest.Variables = variables 264 testMap[secretEnvTest.Name] = secretEnvTest 265 } 266 } 267 268 if len(testMap) == 0 { 269 testMap[test.Name] = test 270 } 271 272 for _, envTest := range testMap { 273 envTests = append(envTests, envTest) 274 } 275 } 276 277 return envTests 278 } 279 280 // generateCRDs generates CRDs for tests 281 func generateCRDs(envTests []client.UpsertTestOptions) { 282 firstEntry := true 283 for _, test := range envTests { 284 if !firstEntry { 285 fmt.Printf("\n---\n") 286 } else { 287 firstEntry = false 288 } 289 290 yaml, err := crd.ExecuteTemplate(crd.TemplateTest, test) 291 ui.ExitOnError("executing test template", err) 292 293 fmt.Print(yaml) 294 } 295 }