github.com/kubeshop/testkube@v1.17.23/pkg/tcl/testworkflowstcl/testworkflowprocessor/initprocess.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 "errors" 13 "fmt" 14 "maps" 15 "strconv" 16 "strings" 17 18 testworkflowsv1 "github.com/kubeshop/testkube-operator/api/testworkflows/v1" 19 "github.com/kubeshop/testkube/cmd/tcl/testworkflow-init/constants" 20 "github.com/kubeshop/testkube/internal/common" 21 "github.com/kubeshop/testkube/pkg/tcl/expressionstcl" 22 constants2 "github.com/kubeshop/testkube/pkg/tcl/testworkflowstcl/testworkflowprocessor/constants" 23 ) 24 25 type initProcess struct { 26 ref string 27 init []string 28 params []string 29 retry map[string]testworkflowsv1.RetryPolicy 30 command []string 31 args []string 32 envs []string 33 results []string 34 conditions map[string][]string 35 negative bool 36 errors []error 37 } 38 39 func NewInitProcess() *initProcess { 40 return &initProcess{ 41 conditions: map[string][]string{}, 42 retry: map[string]testworkflowsv1.RetryPolicy{}, 43 } 44 } 45 46 func (p *initProcess) Error() error { 47 if len(p.errors) == 0 { 48 return nil 49 } 50 return errors.Join(p.errors...) 51 } 52 53 func (p *initProcess) SetRef(ref string) *initProcess { 54 p.ref = ref 55 return p 56 } 57 58 func (p *initProcess) Command() []string { 59 args := p.params 60 61 // TODO: Support nested retries 62 policy, ok := p.retry[p.ref] 63 if ok { 64 args = append(args, constants.ArgRetryCount, strconv.Itoa(int(policy.Count)), constants.ArgRetryUntil, expressionstcl.Escape(policy.Until)) 65 } 66 if p.negative { 67 args = append(args, constants.ArgNegative, "true") 68 } 69 if len(p.init) > 0 { 70 args = append(args, constants.ArgInit, strings.Join(p.init, "&&")) 71 } 72 if len(p.envs) > 0 { 73 args = append(args, constants.ArgComputeEnv, strings.Join(p.envs, ",")) 74 } 75 if len(p.conditions) > 0 { 76 for k, v := range p.conditions { 77 args = append(args, constants.ArgCondition, fmt.Sprintf("%s=%s", strings.Join(common.UniqueSlice(v), ","), k)) 78 } 79 } 80 for _, r := range p.results { 81 args = append(args, constants.ArgResult, r) 82 } 83 return append([]string{constants2.DefaultInitPath, p.ref}, append(args, constants.ArgSeparator)...) 84 } 85 86 func (p *initProcess) Args() []string { 87 args := make([]string, 0) 88 if len(p.command) > 0 { 89 args = p.command 90 } 91 if len(p.command) > 0 || len(p.args) > 0 { 92 args = append(args, p.args...) 93 } 94 return args 95 } 96 97 func (p *initProcess) param(args ...string) *initProcess { 98 p.params = append(p.params, args...) 99 return p 100 } 101 102 func (p *initProcess) compile(expr ...string) []string { 103 for i, e := range expr { 104 res, err := expressionstcl.Compile(e) 105 if err == nil { 106 expr[i] = res.String() 107 } else { 108 p.errors = append(p.errors, fmt.Errorf("resolving expression: %s: %s", expr[i], err.Error())) 109 } 110 } 111 return expr 112 } 113 114 func (p *initProcess) SetCommand(command ...string) *initProcess { 115 p.command = command 116 return p 117 } 118 119 func (p *initProcess) SetArgs(args ...string) *initProcess { 120 p.args = args 121 return p 122 } 123 124 func (p *initProcess) AddTimeout(duration string, refs ...string) *initProcess { 125 return p.param(constants.ArgTimeout, fmt.Sprintf("%s=%s", strings.Join(refs, ","), duration)) 126 } 127 128 func (p *initProcess) SetInitialStatus(expr ...string) *initProcess { 129 p.init = nil 130 for _, v := range p.compile(expr...) { 131 p.init = append(p.init, v) 132 } 133 return p 134 } 135 136 func (p *initProcess) PrependInitialStatus(expr ...string) *initProcess { 137 init := []string(nil) 138 for _, v := range p.compile(expr...) { 139 init = append(init, v) 140 } 141 p.init = append(init, p.init...) 142 return p 143 } 144 145 func (p *initProcess) AddComputedEnvs(names ...string) *initProcess { 146 p.envs = append(p.envs, names...) 147 return p 148 } 149 150 func (p *initProcess) SetNegative(negative bool) *initProcess { 151 p.negative = negative 152 return p 153 } 154 155 func (p *initProcess) AddResult(condition string, refs ...string) *initProcess { 156 if len(refs) == 0 || condition == "" { 157 return p 158 } 159 p.results = append(p.results, fmt.Sprintf("%s=%s", strings.Join(refs, ","), p.compile(condition)[0])) 160 return p 161 } 162 163 func (p *initProcess) ResetResults() *initProcess { 164 p.results = nil 165 return p 166 } 167 168 func (p *initProcess) AddCondition(condition string, refs ...string) *initProcess { 169 if len(refs) == 0 || condition == "" { 170 return p 171 } 172 expr := p.compile(condition)[0] 173 p.conditions[expr] = append(p.conditions[expr], refs...) 174 return p 175 } 176 177 func (p *initProcess) ResetCondition() *initProcess { 178 p.conditions = make(map[string][]string) 179 return p 180 } 181 182 func (p *initProcess) AddRetryPolicy(policy testworkflowsv1.RetryPolicy, ref string) *initProcess { 183 if policy.Count <= 0 { 184 delete(p.retry, ref) 185 return p 186 } 187 until := policy.Until 188 if until == "" { 189 until = "passed" 190 } 191 p.retry[ref] = testworkflowsv1.RetryPolicy{Count: policy.Count, Until: until} 192 return p 193 } 194 195 func (p *initProcess) Children(ref string) *initProcess { 196 return &initProcess{ 197 ref: ref, 198 params: p.params, 199 retry: maps.Clone(p.retry), 200 command: p.command, 201 args: p.args, 202 init: p.init, 203 envs: p.envs, 204 results: p.results, 205 conditions: maps.Clone(p.conditions), 206 negative: p.negative, 207 errors: p.errors, 208 } 209 }