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  }