github.com/kubeshop/testkube@v1.17.23/pkg/tcl/testworkflowstcl/testworkflowprocessor/utils.go (about) 1 // Copyright 2024 Testkube. 2 // 3 // Licensed as a Testkube Pro file under the Testkube Community 4 // License (the "License"); you may not use this file except in compliance with 5 // the License. You may obtain a copy of the License at 6 // 7 // https://github.com/kubeshop/testkube/blob/main/licenses/TCL.txt 8 9 package testworkflowprocessor 10 11 import ( 12 "fmt" 13 "strings" 14 15 batchv1 "k8s.io/api/batch/v1" 16 corev1 "k8s.io/api/core/v1" 17 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 19 "github.com/kubeshop/testkube/internal/common" 20 "github.com/kubeshop/testkube/pkg/tcl/expressionstcl" 21 "github.com/kubeshop/testkube/pkg/tcl/testworkflowstcl/testworkflowprocessor/constants" 22 ) 23 24 func AnnotateControlledBy(obj metav1.Object, testWorkflowId string) { 25 labels := obj.GetLabels() 26 if labels == nil { 27 labels = map[string]string{} 28 } 29 labels[constants.ExecutionIdLabelName] = testWorkflowId 30 obj.SetLabels(labels) 31 32 // Annotate Pod template in the Job 33 if v, ok := obj.(*batchv1.Job); ok { 34 AnnotateControlledBy(&v.Spec.Template, testWorkflowId) 35 } 36 } 37 38 func getRef(stage Stage) string { 39 return stage.Ref() 40 } 41 42 func isNotOptional(stage Stage) bool { 43 return !stage.Optional() 44 } 45 46 func buildKubernetesContainers(stage Stage, init *initProcess, machines ...expressionstcl.Machine) (containers []corev1.Container, err error) { 47 if stage.Timeout() != "" { 48 init.AddTimeout(stage.Timeout(), stage.Ref()) 49 } 50 if stage.Ref() != "" { 51 init.AddCondition(stage.Condition(), stage.Ref()) 52 } 53 init.AddRetryPolicy(stage.RetryPolicy(), stage.Ref()) 54 55 group, ok := stage.(GroupStage) 56 if ok { 57 recursiveRefs := common.MapSlice(group.RecursiveChildren(), getRef) 58 directRefResults := common.MapSlice(common.FilterSlice(group.Children(), isNotOptional), getRef) 59 60 init.AddCondition(stage.Condition(), recursiveRefs...) 61 62 if group.Negative() { 63 // Create virtual layer that will be put down into actual negative step 64 init.SetRef(stage.Ref() + ".v") 65 init.AddCondition(stage.Condition(), stage.Ref()+".v") 66 init.PrependInitialStatus(stage.Ref() + ".v") 67 init.AddResult("!"+stage.Ref()+".v", stage.Ref()) 68 } else if stage.Ref() != "" { 69 init.PrependInitialStatus(stage.Ref()) 70 } 71 72 if group.Optional() { 73 init.ResetResults() 74 } 75 76 if group.Negative() { 77 init.AddResult(strings.Join(directRefResults, "&&"), ""+stage.Ref()+".v") 78 } else { 79 init.AddResult(strings.Join(directRefResults, "&&"), ""+stage.Ref()) 80 } 81 82 for i, ch := range group.Children() { 83 // Condition should be executed only in the first leaf 84 if i == 1 { 85 init.ResetCondition() 86 } 87 // Pass down to another group or container 88 sub, serr := buildKubernetesContainers(ch, init.Children(ch.Ref()), machines...) 89 if serr != nil { 90 return nil, fmt.Errorf("%s: %s: resolving children: %s", stage.Ref(), stage.Name(), serr.Error()) 91 } 92 containers = append(containers, sub...) 93 } 94 return 95 } 96 c, ok := stage.(ContainerStage) 97 if !ok { 98 return nil, fmt.Errorf("%s: %s: stage that is neither container nor group", stage.Ref(), stage.Name()) 99 } 100 err = c.Container().Detach().Resolve(machines...) 101 if err != nil { 102 return nil, fmt.Errorf("%s: %s: resolving container: %s", stage.Ref(), stage.Name(), err.Error()) 103 } 104 105 cr, err := c.Container().ToKubernetesTemplate() 106 if err != nil { 107 return nil, fmt.Errorf("%s: %s: building container template: %s", stage.Ref(), stage.Name(), err.Error()) 108 } 109 cr.Name = c.Ref() 110 111 if c.Optional() { 112 init.ResetResults() 113 } 114 115 init. 116 SetNegative(c.Negative()). 117 AddRetryPolicy(c.RetryPolicy(), c.Ref()). 118 SetCommand(cr.Command...). 119 SetArgs(cr.Args...) 120 121 for _, env := range cr.Env { 122 if strings.Contains(env.Value, "{{") { 123 init.AddComputedEnvs(env.Name) 124 } 125 } 126 127 if init.Error() != nil { 128 return nil, init.Error() 129 } 130 131 cr.Command = init.Command() 132 cr.Args = init.Args() 133 134 // Ensure the container will have proper access to FS 135 if cr.SecurityContext == nil { 136 cr.SecurityContext = &corev1.SecurityContext{} 137 } 138 if cr.SecurityContext.RunAsGroup == nil { 139 cr.SecurityContext.RunAsGroup = common.Ptr(constants.DefaultFsGroup) 140 } 141 142 containers = []corev1.Container{cr} 143 return 144 }