diff --git a/api/testworkflows/v1/types.go b/api/testworkflows/v1/types.go index 5d43dbe8..a7ec1ba3 100644 --- a/api/testworkflows/v1/types.go +++ b/api/testworkflows/v1/types.go @@ -222,6 +222,8 @@ type CronJobConfig struct { Labels map[string]string `json:"labels,omitempty" expr:"template,template"` // annotations to attach to the cron job Annotations map[string]string `json:"annotations,omitempty" expr:"template,template"` + // configuration to pass for the workflow + Config map[string]intstr.IntOrString `json:"config,omitempty" expr:"template,template"` } type TestWorkflowTagSchema struct { diff --git a/api/testworkflows/v1/zz_generated.deepcopy.go b/api/testworkflows/v1/zz_generated.deepcopy.go index 8d6cdc35..5be4023f 100644 --- a/api/testworkflows/v1/zz_generated.deepcopy.go +++ b/api/testworkflows/v1/zz_generated.deepcopy.go @@ -242,6 +242,13 @@ func (in *CronJobConfig) DeepCopyInto(out *CronJobConfig) { (*out)[key] = val } } + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = make(map[string]intstr.IntOrString, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronJobConfig. diff --git a/config/crd/bases/testworkflows.testkube.io_testworkflows.yaml b/config/crd/bases/testworkflows.testkube.io_testworkflows.yaml index 2646beeb..ddb14a5f 100644 --- a/config/crd/bases/testworkflows.testkube.io_testworkflows.yaml +++ b/config/crd/bases/testworkflows.testkube.io_testworkflows.yaml @@ -3581,6 +3581,14 @@ spec: type: string description: annotations to attach to the cron job type: object + config: + additionalProperties: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + description: configuration to pass for the workflow + type: object cron: description: cron schedule to run a test workflow type: string diff --git a/config/crd/bases/testworkflows.testkube.io_testworkflowtemplates.yaml b/config/crd/bases/testworkflows.testkube.io_testworkflowtemplates.yaml index c6d72f67..f300bfdf 100644 --- a/config/crd/bases/testworkflows.testkube.io_testworkflowtemplates.yaml +++ b/config/crd/bases/testworkflows.testkube.io_testworkflowtemplates.yaml @@ -3507,6 +3507,14 @@ spec: type: string description: annotations to attach to the cron job type: object + config: + additionalProperties: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + description: configuration to pass for the workflow + type: object cron: description: cron schedule to run a test workflow type: string diff --git a/internal/controller/testworkflows/testworkflow_controller.go b/internal/controller/testworkflows/testworkflow_controller.go index f3ad3fed..01c38b3d 100644 --- a/internal/controller/testworkflows/testworkflow_controller.go +++ b/internal/controller/testworkflows/testworkflow_controller.go @@ -119,26 +119,32 @@ func (r *TestWorkflowReconciler) Reconcile(ctx context.Context, req ctrl.Request } for i := range cronJobList.Items { - oldCronJobs[cronJobList.Items[i].Spec.Schedule] = &cronJobList.Items[i] + oldCronJobs[cronJobList.Items[i].Name] = &cronJobList.Items[i] } for _, event := range events { if event.Cronjob != nil { - if cronJob, ok := newCronJobConfigs[event.Cronjob.Cron]; !ok { - newCronJobConfigs[event.Cronjob.Cron] = &testworkflowsv1.CronJobConfig{ + name, err := cronjob.GetHashedMetadataName(testWorkflow.Name, event.Cronjob.Cron, event.Cronjob.Config) + if err != nil { + return ctrl.Result{}, err + } + + if cronJob, ok := newCronJobConfigs[name]; !ok { + newCronJobConfigs[name] = &testworkflowsv1.CronJobConfig{ Cron: event.Cronjob.Cron, Labels: event.Cronjob.Labels, Annotations: event.Cronjob.Annotations, + Config: event.Cronjob.Config, } } else { - newCronJobConfigs[cronJob.Cron] = MergeCronJobJobConfig(cronJob, event.Cronjob) + newCronJobConfigs[name] = MergeCronJobJobConfig(cronJob, event.Cronjob) } } } interface_ := testworkflowsv1.API_TestWorkflowRunningContextInterfaceType actor := testworkflowsv1.CRON_TestWorkflowRunningContextActorType - data, err := json.Marshal(testworkflowsv1.TestWorkflowExecutionRequest{ + request := testworkflowsv1.TestWorkflowExecutionRequest{ RunningContext: &testworkflowsv1.TestWorkflowRunningContext{ Interface_: &testworkflowsv1.TestWorkflowRunningContextInterface{ Type_: &interface_, @@ -147,16 +153,12 @@ func (r *TestWorkflowReconciler) Reconcile(ctx context.Context, req ctrl.Request Type_: &actor, }, }, - }) - if err != nil { - return ctrl.Result{}, err } - for schedule, oldCronJob := range oldCronJobs { - if newCronJobConfig, ok := newCronJobConfigs[schedule]; !ok { + for name, oldCronJob := range oldCronJobs { + if newCronJobConfig, ok := newCronJobConfigs[name]; !ok { // Delete removed Cron Jobs - if err = r.CronJobClient.Delete(ctx, - cronjob.GetHashedMetadataName(testWorkflow.Name, schedule), testWorkflow.Namespace); err != nil { + if err = r.CronJobClient.Delete(ctx, name, testWorkflow.Namespace); err != nil { return ctrl.Result{}, err } } else { @@ -165,9 +167,15 @@ func (r *TestWorkflowReconciler) Reconcile(ctx context.Context, req ctrl.Request newCronJobConfig.Labels = make(map[string]string) } + request.Config = newCronJobConfig.Config + data, err := json.Marshal(request) + if err != nil { + return ctrl.Result{}, err + } + newCronJobConfig.Labels[cronjob.TestWorkflowResourceURI] = testWorkflow.Name options := cronjob.CronJobOptions{ - Schedule: schedule, + Schedule: newCronJobConfig.Cron, Group: testworkflowsv1.Group, Resource: testworkflowsv1.Resource, Version: testworkflowsv1.Version, @@ -177,24 +185,29 @@ func (r *TestWorkflowReconciler) Reconcile(ctx context.Context, req ctrl.Request Data: string(data), } - if err = r.CronJobClient.Update(ctx, oldCronJob, testWorkflow.Name, - cronjob.GetHashedMetadataName(testWorkflow.Name, schedule), testWorkflow.Namespace, + if err = r.CronJobClient.Update(ctx, oldCronJob, testWorkflow.Name, name, testWorkflow.Namespace, string(testWorkflow.UID), options); err != nil { return ctrl.Result{}, err } } } - for schedule, newCronJobConfig := range newCronJobConfigs { - if _, ok = oldCronJobs[schedule]; !ok { + for name, newCronJobConfig := range newCronJobConfigs { + if _, ok = oldCronJobs[name]; !ok { // Create new Cron Jobs if newCronJobConfig.Labels == nil { newCronJobConfig.Labels = make(map[string]string) } + request.Config = newCronJobConfig.Config + data, err := json.Marshal(request) + if err != nil { + return ctrl.Result{}, err + } + newCronJobConfig.Labels[cronjob.TestWorkflowResourceURI] = testWorkflow.Name options := cronjob.CronJobOptions{ - Schedule: schedule, + Schedule: newCronJobConfig.Cron, Group: testworkflowsv1.Group, Resource: testworkflowsv1.Resource, Version: testworkflowsv1.Version, @@ -204,8 +217,7 @@ func (r *TestWorkflowReconciler) Reconcile(ctx context.Context, req ctrl.Request Data: string(data), } - if err = r.CronJobClient.Create(ctx, testWorkflow.Name, - cronjob.GetHashedMetadataName(testWorkflow.Name, schedule), testWorkflow.Namespace, + if err = r.CronJobClient.Create(ctx, testWorkflow.Name, name, testWorkflow.Namespace, string(testWorkflow.UID), options); err != nil { return ctrl.Result{}, err } diff --git a/pkg/cronjob/client.go b/pkg/cronjob/client.go index 3796de63..9a14de66 100644 --- a/pkg/cronjob/client.go +++ b/pkg/cronjob/client.go @@ -3,6 +3,7 @@ package cronjob import ( "bytes" "context" + "encoding/json" "fmt" "hash/fnv" "maps" @@ -14,6 +15,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/yaml" "sigs.k8s.io/controller-runtime/pkg/client" kyaml "sigs.k8s.io/kustomize/kyaml/yaml" @@ -294,17 +296,23 @@ func GetMetadataName(name, resource string) string { } // GetHashedMetadataName returns cron job hashed metadata name -func GetHashedMetadataName(name, schedule string) string { - h := fnv.New32a() +func GetHashedMetadataName(name, schedule string, config map[string]intstr.IntOrString) (string, error) { + data, err := json.Marshal(config) + if err != nil { + return "", err + } + + h := fnv.New64a() h.Write([]byte(schedule)) + h.Write(data) - hash := fmt.Sprintf("-%d", h.Sum32()) + hash := fmt.Sprintf("-%d", h.Sum64()) if len(name) > 52-len(hash) { name = name[:52-len(hash)] } - return name + hash + return name + hash, nil } // GetSelector returns cron job selecttor