From 2738766411db76c073e11c215a7e21dd372dea63 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> Date: Tue, 15 Apr 2025 09:26:57 +0200 Subject: [PATCH 01/47] expose worker and apps using service type ClusterIP Signed-off-by: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> --- functions/onprem/orborus/orborus.go | 12 ++++----- functions/onprem/worker/worker.go | 42 ++++++++++++++--------------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 1f37da85..f09b9a37 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -747,7 +747,7 @@ func handleBackendImageDownload(ctx context.Context, images string) error { //log.Printf("[DEBUG] Removing existing image (s): %s", images) newImages := []string{} - successful := []string{} + successful := []string{} for _, curimage := range strings.Split(images, ",") { curimage = strings.TrimSpace(curimage) if shuffle.ArrayContains(handled, curimage) { @@ -1067,9 +1067,9 @@ func deployK8sWorker(image string, identifier string, env []string) error { } labels := map[string]string{ - "app.kubernetes.io/name": "shuffle-worker", - "app.kubernetes.io/instance": identifier, - // "app.kubernetes.io/version": "", + // Well-known Kubernetes labels + "app.kubernetes.io/name": "shuffle-worker", + "app.kubernetes.io/instance": identifier, "app.kubernetes.io/part-of": "shuffle", "app.kubernetes.io/managed-by": "shuffle-orborus", // Keep legacy labels for backward compatibility @@ -1212,7 +1212,6 @@ func deployK8sWorker(image string, identifier string, env []string) error { return err } - // kubectl expose deployment shuffle-workers --type=NodePort --port=33333 --target-port=33333 service := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: identifier, @@ -1227,7 +1226,7 @@ func deployK8sWorker(image string, identifier string, env []string) error { TargetPort: intstr.FromInt(33333), }, }, - Type: corev1.ServiceTypeNodePort, + Type: corev1.ServiceTypeClusterIP, }, } @@ -1271,7 +1270,6 @@ func deployWorker(image string, identifier string, env []string, executionReques Resources: container.Resources{}, } - // This is just to test the mounting locally so // I can control from what source I'm mounting // the certs to. Default behaviour is: diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index a8f1c204..8cc47cba 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -489,13 +489,16 @@ func deployk8sApp(image string, identifier string, env []string) error { name := strings.ReplaceAll(identifier, "_", "-") labels := map[string]string{ - "app.kubernetes.io/name": "shuffle-app", - "app.kubernetes.io/instance": name, - // "app.kubernetes.io/version": "", + // Well-known Kubernetes labels + "app.kubernetes.io/name": "shuffle-app", + "app.kubernetes.io/instance": name, "app.kubernetes.io/part-of": "shuffle", "app.kubernetes.io/managed-by": "shuffle-worker", // Keep legacy labels for backward compatibility "app": name, + // TODO: Add Shuffle specific labels + // "app.shuffler.io/name": "APP_NAME", + // "app.shuffler.io/version": "APP_VERSION", } matchLabels := map[string]string{ @@ -614,7 +617,6 @@ func deployk8sApp(image string, identifier string, env []string) error { return err } - // kubectl expose deployment {podName} --type=NodePort --port=80 --target-port=80 service := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -629,7 +631,7 @@ func deployk8sApp(image string, identifier string, env []string) error { TargetPort: intstr.FromInt(80), }, }, - Type: corev1.ServiceTypeNodePort, + Type: corev1.ServiceTypeClusterIP, }, } @@ -917,7 +919,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] // Add more volume binds if possible if len(volumeBinds) > 0 { - // Only use mounts, not direct binds + // Only use mounts, not direct binds hostConfig.Binds = []string{} hostConfig.Mounts = []mount.Mount{} for _, bind := range volumeBinds { @@ -931,7 +933,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] sourceFolder := bindSplit[0] destinationFolder := bindSplit[1] - readOnly := false + readOnly := false if len(bindSplit) > 2 { mode := bindSplit[2] if mode == "ro" { @@ -940,9 +942,9 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] } builtMount := mount.Mount{ - Type: mount.TypeBind, - Source: sourceFolder, - Target: destinationFolder, + Type: mount.TypeBind, + Source: sourceFolder, + Target: destinationFolder, ReadOnly: readOnly, } @@ -1853,18 +1855,18 @@ func executionInit(workflowExecution shuffle.WorkflowExecution) error { } } - // Validates RERUN of single actions - // Identified by: + // Validates RERUN of single actions + // Identified by: // 1. Predefined result from previous exec // 2. Only ONE action // 3. Every predefined result having result.Action.Category == "rerun" /* - if len(workflowExecution.Workflow.Actions) == 1 && len(workflowExecution.Results) > 0 { - finished := shuffle.ValidateFinished(ctx, extra, workflowExecution) - if finished { - return nil + if len(workflowExecution.Workflow.Actions) == 1 && len(workflowExecution.Results) > 0 { + finished := shuffle.ValidateFinished(ctx, extra, workflowExecution) + if finished { + return nil + } } - } */ nextActions = append(nextActions, startAction) @@ -1954,7 +1956,6 @@ func executionInit(workflowExecution shuffle.WorkflowExecution) error { //log.Printf("Successfully downloaded and built %s", image) } - visited := []string{} executed := []string{} environments := []string{} @@ -3777,7 +3778,7 @@ func checkStandaloneRun() { if !strings.Contains(backendUrl, "http") { log.Printf("[ERROR] Backend URL should start with http:// or https://") return - + } // Format: @@ -3851,7 +3852,7 @@ func checkStandaloneRun() { continue } - // This is to handle reruns of SINGLE actions + // This is to handle reruns of SINGLE actions if result.Action.Category == "rerun" { newResults = append(newResults, result) continue @@ -3905,7 +3906,6 @@ func checkStandaloneRun() { log.Printf("\n\n\n[DEBUG] Finished resetting execution %s. Body: %s. Starting execution.\n\n\n", newresp.Status, string(body)) - } // Initial loop etc From 3491ba6471630152acde240f679fdd2237713bc7 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> Date: Wed, 16 Apr 2025 11:34:41 +0200 Subject: [PATCH 02/47] feat(k8s): allow to change exposed app port Signed-off-by: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> --- functions/onprem/orborus/orborus.go | 7 +++-- functions/onprem/worker/worker.go | 48 ++++++++++++++++------------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 1f37da85..36e7979e 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -747,7 +747,7 @@ func handleBackendImageDownload(ctx context.Context, images string) error { //log.Printf("[DEBUG] Removing existing image (s): %s", images) newImages := []string{} - successful := []string{} + successful := []string{} for _, curimage := range strings.Split(images, ",") { curimage = strings.TrimSpace(curimage) if shuffle.ArrayContains(handled, curimage) { @@ -996,6 +996,10 @@ func deployK8sWorker(image string, identifier string, env []string) error { env = append(env, fmt.Sprintf("SHUFFLE_USE_GHCR_OVERRIDE_FOR_AUTODEPLOY=%s", os.Getenv("SHUFFLE_USE_GHCR_OVERRIDE_FOR_AUTODEPLOY"))) } + if len(os.Getenv("SHUFFLE_APP_EXPOSED_PORT")) > 0 { + env = append(env, fmt.Sprintf("SHUFFLE_APP_EXPOSED_PORT=%s", os.Getenv("SHUFFLE_APP_EXPOSED_PORT"))) + } + if len(appServiceAccountName) > 0 { env = append(env, fmt.Sprintf("SHUFFLE_APP_SERVICE_ACCOUNT_NAME=%s", appServiceAccountName)) } @@ -1271,7 +1275,6 @@ func deployWorker(image string, identifier string, env []string, executionReques Resources: container.Resources{}, } - // This is just to test the mounting locally so // I can control from what source I'm mounting // the certs to. Default behaviour is: diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index a8f1c204..c411ae52 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -399,6 +399,11 @@ func deployk8sApp(image string, identifier string, env []string) error { kubernetesNamespace = "default" } + deployport, err := strconv.Atoi(os.Getenv("SHUFFLE_APP_EXPOSED_PORT")) + if err != nil { + deployport = 80 + } + envMap := make(map[string]string) for _, envStr := range env { parts := strings.SplitN(envStr, "=", 2) @@ -408,9 +413,7 @@ func deployk8sApp(image string, identifier string, env []string) error { } // add to env - // fmt.Sprintf("SHUFFLE_APP_EXPOSED_PORT=%d", deployport), - // fmt.Sprintf("SHUFFLE_SWARM_CONFIG=%s", os.Getenv("SHUFFLE_SWARM_CONFIG")), - envMap["SHUFFLE_APP_EXPOSED_PORT"] = "80" + envMap["SHUFFLE_APP_EXPOSED_PORT"] = strconv.Itoa(deployport) envMap["SHUFFLE_SWARM_CONFIG"] = os.Getenv("SHUFFLE_SWARM_CONFIG") envMap["BASE_URL"] = "http://shuffle-workers:33333" @@ -599,6 +602,12 @@ func deployk8sApp(image string, identifier string, env []string) error { Name: value, Image: image, Env: buildEnvVars(envMap), + Ports: []corev1.ContainerPort{ + { + Protocol: "TCP", + ContainerPort: int32(deployport), + }, + }, }, }, DNSPolicy: corev1.DNSClusterFirst, @@ -614,7 +623,6 @@ func deployk8sApp(image string, identifier string, env []string) error { return err } - // kubectl expose deployment {podName} --type=NodePort --port=80 --target-port=80 service := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -626,7 +634,7 @@ func deployk8sApp(image string, identifier string, env []string) error { { Protocol: "TCP", Port: 80, - TargetPort: intstr.FromInt(80), + TargetPort: intstr.FromInt(deployport), }, }, Type: corev1.ServiceTypeNodePort, @@ -917,7 +925,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] // Add more volume binds if possible if len(volumeBinds) > 0 { - // Only use mounts, not direct binds + // Only use mounts, not direct binds hostConfig.Binds = []string{} hostConfig.Mounts = []mount.Mount{} for _, bind := range volumeBinds { @@ -931,7 +939,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] sourceFolder := bindSplit[0] destinationFolder := bindSplit[1] - readOnly := false + readOnly := false if len(bindSplit) > 2 { mode := bindSplit[2] if mode == "ro" { @@ -940,9 +948,9 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] } builtMount := mount.Mount{ - Type: mount.TypeBind, - Source: sourceFolder, - Target: destinationFolder, + Type: mount.TypeBind, + Source: sourceFolder, + Target: destinationFolder, ReadOnly: readOnly, } @@ -1853,18 +1861,18 @@ func executionInit(workflowExecution shuffle.WorkflowExecution) error { } } - // Validates RERUN of single actions - // Identified by: + // Validates RERUN of single actions + // Identified by: // 1. Predefined result from previous exec // 2. Only ONE action // 3. Every predefined result having result.Action.Category == "rerun" /* - if len(workflowExecution.Workflow.Actions) == 1 && len(workflowExecution.Results) > 0 { - finished := shuffle.ValidateFinished(ctx, extra, workflowExecution) - if finished { - return nil + if len(workflowExecution.Workflow.Actions) == 1 && len(workflowExecution.Results) > 0 { + finished := shuffle.ValidateFinished(ctx, extra, workflowExecution) + if finished { + return nil + } } - } */ nextActions = append(nextActions, startAction) @@ -1954,7 +1962,6 @@ func executionInit(workflowExecution shuffle.WorkflowExecution) error { //log.Printf("Successfully downloaded and built %s", image) } - visited := []string{} executed := []string{} environments := []string{} @@ -3777,7 +3784,7 @@ func checkStandaloneRun() { if !strings.Contains(backendUrl, "http") { log.Printf("[ERROR] Backend URL should start with http:// or https://") return - + } // Format: @@ -3851,7 +3858,7 @@ func checkStandaloneRun() { continue } - // This is to handle reruns of SINGLE actions + // This is to handle reruns of SINGLE actions if result.Action.Category == "rerun" { newResults = append(newResults, result) continue @@ -3905,7 +3912,6 @@ func checkStandaloneRun() { log.Printf("\n\n\n[DEBUG] Finished resetting execution %s. Body: %s. Starting execution.\n\n\n", newresp.Status, string(body)) - } // Initial loop etc From 9fa02c15b139b8d339d47b50dfababffcc69f799 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> Date: Thu, 17 Apr 2025 09:59:37 +0200 Subject: [PATCH 03/47] feat(k8s): allow to set security contexts for worker and apps Signed-off-by: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> --- functions/kubernetes/charts/shuffle/README.md | 92 ++++++--- .../templates/orborus/orborus-dpl.yaml | 16 ++ .../charts/shuffle/values.schema.json | 194 ++++++++++++++++++ .../kubernetes/charts/shuffle/values.yaml | 84 ++++++++ functions/onprem/orborus/orborus.go | 45 +++- functions/onprem/worker/worker.go | 66 ++++-- 6 files changed, 442 insertions(+), 55 deletions(-) diff --git a/functions/kubernetes/charts/shuffle/README.md b/functions/kubernetes/charts/shuffle/README.md index ff3885d1..5c70ab50 100644 --- a/functions/kubernetes/charts/shuffle/README.md +++ b/functions/kubernetes/charts/shuffle/README.md @@ -477,39 +477,69 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia ### worker Parameters -| Name | Description | Value | -| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | -| `worker.image.registry` | worker image registry | `ghcr.io` | -| `worker.image.repository` | worker image repository | `shuffle/shuffle-worker` | -| `worker.image.tag` | worker image tag (immutable tags are recommended, defaults to appVersion) | `""` | -| `worker.image.digest` | worker image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag image tag (immutable tags are recommended) | `""` | -| `worker.serviceAccount.create` | Specifies whether a ServiceAccount should be created | `true` | -| `worker.serviceAccount.name` | The name of the ServiceAccount to use. | `""` | -| `worker.serviceAccount.annotations` | Additional Service Account annotations (evaluated as a template) | `{}` | -| `worker.serviceAccount.automountServiceAccountToken` | Automount service account token for the worker service account | `true` | -| `worker.serviceAccount.imagePullSecrets` | Add image pull secrets to the worker service account | `[]` | -| `worker.rbac.create` | Specifies whether RBAC resources should be created | `true` | -| `worker.networkPolicy.enabled` | Specifies whether a NetworkPolicy should be created | `true` | -| `worker.networkPolicy.allowExternal` | Don't require server label for connections | `true` | -| `worker.networkPolicy.allowExternalEgress` | Allow the pod to access any range of port and all destinations. | `true` | -| `worker.networkPolicy.extraIngress` | Add extra ingress rules to the NetworkPolicy | `[]` | -| `worker.networkPolicy.extraEgress` | Add extra ingress rules to the NetworkPolicy (ignored if allowExternalEgress=true) | `[]` | +| Name | Description | Value | +| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| `worker.image.registry` | worker image registry | `ghcr.io` | +| `worker.image.repository` | worker image repository | `shuffle/shuffle-worker` | +| `worker.image.tag` | worker image tag (immutable tags are recommended, defaults to appVersion) | `""` | +| `worker.image.digest` | worker image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag image tag (immutable tags are recommended) | `""` | +| `worker.podSecurityContext.enabled` | Enable worker pods' Security Context | `true` | +| `worker.podSecurityContext.fsGroupChangePolicy` | Set filesystem group change policy for worker pods | `Always` | +| `worker.podSecurityContext.sysctls` | Set kernel settings using the sysctl interface for worker pods | `[]` | +| `worker.podSecurityContext.supplementalGroups` | Set filesystem extra groups for worker pods | `[]` | +| `worker.podSecurityContext.fsGroup` | Set fsGroup in worker pods' Security Context | `1001` | +| `worker.containerSecurityContext.enabled` | Enabled worker container' Security Context | `true` | +| `worker.containerSecurityContext.seLinuxOptions` | Set SELinux options in worker container | `{}` | +| `worker.containerSecurityContext.runAsUser` | Set runAsUser in worker container' Security Context | `1001` | +| `worker.containerSecurityContext.runAsGroup` | Set runAsGroup in worker container' Security Context | `1001` | +| `worker.containerSecurityContext.runAsNonRoot` | Set runAsNonRoot in worker container' Security Context | `true` | +| `worker.containerSecurityContext.readOnlyRootFilesystem` | Set readOnlyRootFilesystem in worker container' Security Context | `true` | +| `worker.containerSecurityContext.privileged` | Set privileged in worker container' Security Context | `false` | +| `worker.containerSecurityContext.allowPrivilegeEscalation` | Set allowPrivilegeEscalation in worker container' Security Context | `false` | +| `worker.containerSecurityContext.capabilities.drop` | List of capabilities to be dropped in worker container | `["ALL"]` | +| `worker.containerSecurityContext.seccompProfile.type` | Set seccomp profile in worker container | `RuntimeDefault` | +| `worker.serviceAccount.create` | Specifies whether a ServiceAccount should be created | `true` | +| `worker.serviceAccount.name` | The name of the ServiceAccount to use. | `""` | +| `worker.serviceAccount.annotations` | Additional Service Account annotations (evaluated as a template) | `{}` | +| `worker.serviceAccount.automountServiceAccountToken` | Automount service account token for the worker service account | `true` | +| `worker.serviceAccount.imagePullSecrets` | Add image pull secrets to the worker service account | `[]` | +| `worker.rbac.create` | Specifies whether RBAC resources should be created | `true` | +| `worker.networkPolicy.enabled` | Specifies whether a NetworkPolicy should be created | `true` | +| `worker.networkPolicy.allowExternal` | Don't require server label for connections | `true` | +| `worker.networkPolicy.allowExternalEgress` | Allow the pod to access any range of port and all destinations. | `true` | +| `worker.networkPolicy.extraIngress` | Add extra ingress rules to the NetworkPolicy | `[]` | +| `worker.networkPolicy.extraEgress` | Add extra ingress rules to the NetworkPolicy (ignored if allowExternalEgress=true) | `[]` | ### app Parameters -| Name | Description | Value | -| ------------------------------------------------- | ---------------------------------------------------------------------------------- | ------ | -| `app.serviceAccount.create` | Specifies whether a ServiceAccount should be created | `true` | -| `app.serviceAccount.name` | The name of the ServiceAccount to use. | `""` | -| `app.serviceAccount.annotations` | Additional Service Account annotations (evaluated as a template) | `{}` | -| `app.serviceAccount.automountServiceAccountToken` | Automount service account token for the app service account | `true` | -| `app.serviceAccount.imagePullSecrets` | Add image pull secrets to the app service account | `[]` | -| `app.rbac.create` | Specifies whether RBAC resources should be created | `true` | -| `app.networkPolicy.enabled` | Specifies whether a NetworkPolicy should be created | `true` | -| `app.networkPolicy.allowExternal` | Don't require server label for connections | `true` | -| `app.networkPolicy.allowExternalEgress` | Allow the pod to access any range of port and all destinations. | `true` | -| `app.networkPolicy.extraIngress` | Add extra ingress rules to the NetworkPolicy | `[]` | -| `app.networkPolicy.extraEgress` | Add extra ingress rules to the NetworkPolicy (ignored if allowExternalEgress=true) | `[]` | +| Name | Description | Value | +| ------------------------------------------------------- | ---------------------------------------------------------------------------------- | ---------------- | +| `app.podSecurityContext.enabled` | Enable app pods' Security Context | `true` | +| `app.podSecurityContext.fsGroupChangePolicy` | Set filesystem group change policy for app pods | `Always` | +| `app.podSecurityContext.sysctls` | Set kernel settings using the sysctl interface for app pods | `[]` | +| `app.podSecurityContext.supplementalGroups` | Set filesystem extra groups for app pods | `[]` | +| `app.podSecurityContext.fsGroup` | Set fsGroup in app pods' Security Context | `1001` | +| `app.containerSecurityContext.enabled` | Enabled app container' Security Context | `true` | +| `app.containerSecurityContext.seLinuxOptions` | Set SELinux options in app container | `{}` | +| `app.containerSecurityContext.runAsUser` | Set runAsUser in app container' Security Context | `1001` | +| `app.containerSecurityContext.runAsGroup` | Set runAsGroup in app container' Security Context | `1001` | +| `app.containerSecurityContext.runAsNonRoot` | Set runAsNonRoot in app container' Security Context | `true` | +| `app.containerSecurityContext.readOnlyRootFilesystem` | Set readOnlyRootFilesystem in app container' Security Context | `true` | +| `app.containerSecurityContext.privileged` | Set privileged in app container' Security Context | `false` | +| `app.containerSecurityContext.allowPrivilegeEscalation` | Set allowPrivilegeEscalation in app container' Security Context | `false` | +| `app.containerSecurityContext.capabilities.drop` | List of capabilities to be dropped in app container | `["ALL"]` | +| `app.containerSecurityContext.seccompProfile.type` | Set seccomp profile in app container | `RuntimeDefault` | +| `app.serviceAccount.create` | Specifies whether a ServiceAccount should be created | `true` | +| `app.serviceAccount.name` | The name of the ServiceAccount to use. | `""` | +| `app.serviceAccount.annotations` | Additional Service Account annotations (evaluated as a template) | `{}` | +| `app.serviceAccount.automountServiceAccountToken` | Automount service account token for the app service account | `true` | +| `app.serviceAccount.imagePullSecrets` | Add image pull secrets to the app service account | `[]` | +| `app.rbac.create` | Specifies whether RBAC resources should be created | `true` | +| `app.networkPolicy.enabled` | Specifies whether a NetworkPolicy should be created | `true` | +| `app.networkPolicy.allowExternal` | Don't require server label for connections | `true` | +| `app.networkPolicy.allowExternalEgress` | Allow the pod to access any range of port and all destinations. | `true` | +| `app.networkPolicy.extraIngress` | Add extra ingress rules to the NetworkPolicy | `[]` | +| `app.networkPolicy.extraEgress` | Add extra ingress rules to the NetworkPolicy (ignored if allowExternalEgress=true) | `[]` | ### Traffic Exposure Parameters @@ -607,3 +637,5 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia ### Other Parameters + + diff --git a/functions/kubernetes/charts/shuffle/templates/orborus/orborus-dpl.yaml b/functions/kubernetes/charts/shuffle/templates/orborus/orborus-dpl.yaml index a2d9c278..5911eb19 100644 --- a/functions/kubernetes/charts/shuffle/templates/orborus/orborus-dpl.yaml +++ b/functions/kubernetes/charts/shuffle/templates/orborus/orborus-dpl.yaml @@ -88,8 +88,24 @@ spec: value: "true" - name: SHUFFLE_WORKER_SERVICE_ACCOUNT_NAME value: {{ include "shuffle.worker.serviceAccount.name" . }} + {{- if .Values.worker.podSecurityContext.enabled }} + - name: SHUFFLE_WORKER_POD_SECURITY_CONTEXT + value: {{ omit .Values.worker.podSecurityContext "enabled" | mustToJson | quote }} + {{- end }} + {{- if .Values.worker.containerSecurityContext.enabled }} + - name: SHUFFLE_WORKER_CONTAINER_SECURITY_CONTEXT + value: {{ include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.worker.containerSecurityContext "context" $) | fromYaml | mustToJson | quote }} + {{- end }} - name: SHUFFLE_APP_SERVICE_ACCOUNT_NAME value: {{ include "shuffle.app.serviceAccount.name" . }} + {{- if .Values.app.podSecurityContext.enabled }} + - name: SHUFFLE_APP_POD_SECURITY_CONTEXT + value: {{ omit .Values.app.podSecurityContext "enabled" | mustToJson | quote }} + {{- end }} + {{- if .Values.app.containerSecurityContext.enabled }} + - name: SHUFFLE_APP_CONTAINER_SECURITY_CONTEXT + value: {{ include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.app.containerSecurityContext "context" $) | fromYaml | mustToJson | quote }} + {{- end }} {{- if .Values.orborus.extraEnvVars }} {{- include "common.tplvalues.render" (dict "value" .Values.orborus.extraEnvVars "context" $) | nindent 12 }} {{- end }} diff --git a/functions/kubernetes/charts/shuffle/values.schema.json b/functions/kubernetes/charts/shuffle/values.schema.json index b785b76e..c2848aaa 100644 --- a/functions/kubernetes/charts/shuffle/values.schema.json +++ b/functions/kubernetes/charts/shuffle/values.schema.json @@ -2074,6 +2074,103 @@ } } }, + "podSecurityContext": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable worker pods' Security Context", + "default": true + }, + "fsGroupChangePolicy": { + "type": "string", + "description": "Set filesystem group change policy for worker pods", + "default": "Always" + }, + "sysctls": { + "type": "array", + "description": "Set kernel settings using the sysctl interface for worker pods", + "default": [], + "items": {} + }, + "supplementalGroups": { + "type": "array", + "description": "Set filesystem extra groups for worker pods", + "default": [], + "items": {} + }, + "fsGroup": { + "type": "number", + "description": "Set fsGroup in worker pods' Security Context", + "default": 1001 + } + } + }, + "containerSecurityContext": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enabled worker container' Security Context", + "default": true + }, + "runAsUser": { + "type": "number", + "description": "Set runAsUser in worker container' Security Context", + "default": 1001 + }, + "runAsGroup": { + "type": "number", + "description": "Set runAsGroup in worker container' Security Context", + "default": 1001 + }, + "runAsNonRoot": { + "type": "boolean", + "description": "Set runAsNonRoot in worker container' Security Context", + "default": true + }, + "readOnlyRootFilesystem": { + "type": "boolean", + "description": "Set readOnlyRootFilesystem in worker container' Security Context", + "default": true + }, + "privileged": { + "type": "boolean", + "description": "Set privileged in worker container' Security Context", + "default": false + }, + "allowPrivilegeEscalation": { + "type": "boolean", + "description": "Set allowPrivilegeEscalation in worker container' Security Context", + "default": false + }, + "capabilities": { + "type": "object", + "properties": { + "drop": { + "type": "array", + "description": "List of capabilities to be dropped in worker container", + "default": [ + "ALL" + ], + "items": { + "type": "string" + } + } + } + }, + "seccompProfile": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Set seccomp profile in worker container", + "default": "RuntimeDefault" + } + } + } + } + }, "serviceAccount": { "type": "object", "properties": { @@ -2152,6 +2249,103 @@ "app": { "type": "object", "properties": { + "podSecurityContext": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable app pods' Security Context", + "default": true + }, + "fsGroupChangePolicy": { + "type": "string", + "description": "Set filesystem group change policy for app pods", + "default": "Always" + }, + "sysctls": { + "type": "array", + "description": "Set kernel settings using the sysctl interface for app pods", + "default": [], + "items": {} + }, + "supplementalGroups": { + "type": "array", + "description": "Set filesystem extra groups for app pods", + "default": [], + "items": {} + }, + "fsGroup": { + "type": "number", + "description": "Set fsGroup in app pods' Security Context", + "default": 1001 + } + } + }, + "containerSecurityContext": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enabled app container' Security Context", + "default": true + }, + "runAsUser": { + "type": "number", + "description": "Set runAsUser in app container' Security Context", + "default": 1001 + }, + "runAsGroup": { + "type": "number", + "description": "Set runAsGroup in app container' Security Context", + "default": 1001 + }, + "runAsNonRoot": { + "type": "boolean", + "description": "Set runAsNonRoot in app container' Security Context", + "default": true + }, + "readOnlyRootFilesystem": { + "type": "boolean", + "description": "Set readOnlyRootFilesystem in app container' Security Context", + "default": true + }, + "privileged": { + "type": "boolean", + "description": "Set privileged in app container' Security Context", + "default": false + }, + "allowPrivilegeEscalation": { + "type": "boolean", + "description": "Set allowPrivilegeEscalation in app container' Security Context", + "default": false + }, + "capabilities": { + "type": "object", + "properties": { + "drop": { + "type": "array", + "description": "List of capabilities to be dropped in app container", + "default": [ + "ALL" + ], + "items": { + "type": "string" + } + } + } + }, + "seccompProfile": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Set seccomp profile in app container", + "default": "RuntimeDefault" + } + } + } + } + }, "serviceAccount": { "type": "object", "properties": { diff --git a/functions/kubernetes/charts/shuffle/values.yaml b/functions/kubernetes/charts/shuffle/values.yaml index 507c2468..db11c0e6 100644 --- a/functions/kubernetes/charts/shuffle/values.yaml +++ b/functions/kubernetes/charts/shuffle/values.yaml @@ -1328,6 +1328,48 @@ worker: tag: "" digest: "" + ## Configure Pods Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param worker.podSecurityContext.enabled Enable worker pods' Security Context + ## @param worker.podSecurityContext.fsGroupChangePolicy Set filesystem group change policy for worker pods + ## @param worker.podSecurityContext.sysctls Set kernel settings using the sysctl interface for worker pods + ## @param worker.podSecurityContext.supplementalGroups Set filesystem extra groups for worker pods + ## @param worker.podSecurityContext.fsGroup Set fsGroup in worker pods' Security Context + ## + podSecurityContext: + enabled: true + fsGroupChangePolicy: Always + sysctls: [] + supplementalGroups: [] + fsGroup: 1001 + + ## Configure Container Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container + ## @param worker.containerSecurityContext.enabled Enabled worker container' Security Context + ## @param worker.containerSecurityContext.seLinuxOptions [object,nullable] Set SELinux options in worker container + ## @param worker.containerSecurityContext.runAsUser Set runAsUser in worker container' Security Context + ## @param worker.containerSecurityContext.runAsGroup Set runAsGroup in worker container' Security Context + ## @param worker.containerSecurityContext.runAsNonRoot Set runAsNonRoot in worker container' Security Context + ## @param worker.containerSecurityContext.readOnlyRootFilesystem Set readOnlyRootFilesystem in worker container' Security Context + ## @param worker.containerSecurityContext.privileged Set privileged in worker container' Security Context + ## @param worker.containerSecurityContext.allowPrivilegeEscalation Set allowPrivilegeEscalation in worker container' Security Context + ## @param worker.containerSecurityContext.capabilities.drop List of capabilities to be dropped in worker container + ## @param worker.containerSecurityContext.seccompProfile.type Set seccomp profile in worker container + ## + containerSecurityContext: + enabled: true + seLinuxOptions: {} + runAsUser: 1001 + runAsGroup: 1001 + runAsNonRoot: true + readOnlyRootFilesystem: true + privileged: false + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + seccompProfile: + type: "RuntimeDefault" + ## ServiceAccount configuration ## serviceAccount: @@ -1390,6 +1432,48 @@ worker: ## @section app Parameters ## app: + ## Configure Pods Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param app.podSecurityContext.enabled Enable app pods' Security Context + ## @param app.podSecurityContext.fsGroupChangePolicy Set filesystem group change policy for app pods + ## @param app.podSecurityContext.sysctls Set kernel settings using the sysctl interface for app pods + ## @param app.podSecurityContext.supplementalGroups Set filesystem extra groups for app pods + ## @param app.podSecurityContext.fsGroup Set fsGroup in app pods' Security Context + ## + podSecurityContext: + enabled: true + fsGroupChangePolicy: Always + sysctls: [] + supplementalGroups: [] + fsGroup: 1001 + + ## Configure Container Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container + ## @param app.containerSecurityContext.enabled Enabled app container' Security Context + ## @param app.containerSecurityContext.seLinuxOptions [object,nullable] Set SELinux options in app container + ## @param app.containerSecurityContext.runAsUser Set runAsUser in app container' Security Context + ## @param app.containerSecurityContext.runAsGroup Set runAsGroup in app container' Security Context + ## @param app.containerSecurityContext.runAsNonRoot Set runAsNonRoot in app container' Security Context + ## @param app.containerSecurityContext.readOnlyRootFilesystem Set readOnlyRootFilesystem in app container' Security Context + ## @param app.containerSecurityContext.privileged Set privileged in app container' Security Context + ## @param app.containerSecurityContext.allowPrivilegeEscalation Set allowPrivilegeEscalation in app container' Security Context + ## @param app.containerSecurityContext.capabilities.drop List of capabilities to be dropped in app container + ## @param app.containerSecurityContext.seccompProfile.type Set seccomp profile in app container + ## + containerSecurityContext: + enabled: true + seLinuxOptions: {} + runAsUser: 1001 + runAsGroup: 1001 + runAsNonRoot: true + readOnlyRootFilesystem: true + privileged: false + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + seccompProfile: + type: "RuntimeDefault" + ## ServiceAccount configuration ## serviceAccount: diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 1f37da85..b44ca0c6 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -76,7 +76,11 @@ var maxCPUPercent = 90 var isKubernetes = os.Getenv("IS_KUBERNETES") var kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE") var workerServiceAccountName = os.Getenv("SHUFFLE_WORKER_SERVICE_ACCOUNT_NAME") +var workerPodSecurityContext = os.Getenv("SHUFFLE_WORKER_POD_SECURITY_CONTEXT") +var workerContainerSecurityContext = os.Getenv("SHUFFLE_WORKER_CONTAINER_SECURITY_CONTEXT") var appServiceAccountName = os.Getenv("SHUFFLE_APP_SERVICE_ACCOUNT_NAME") +var appPodSecurityContext = os.Getenv("SHUFFLE_APP_POD_SECURITY_CONTEXT") +var appContainerSecurityContext = os.Getenv("SHUFFLE_APP_CONTAINER_SECURITY_CONTEXT") // var baseimagename = "docker.pkg.github.com/shuffle/shuffle" // var baseimagename = "ghcr.io/frikky" @@ -747,7 +751,7 @@ func handleBackendImageDownload(ctx context.Context, images string) error { //log.Printf("[DEBUG] Removing existing image (s): %s", images) newImages := []string{} - successful := []string{} + successful := []string{} for _, curimage := range strings.Split(images, ",") { curimage = strings.TrimSpace(curimage) if shuffle.ArrayContains(handled, curimage) { @@ -1000,6 +1004,14 @@ func deployK8sWorker(image string, identifier string, env []string) error { env = append(env, fmt.Sprintf("SHUFFLE_APP_SERVICE_ACCOUNT_NAME=%s", appServiceAccountName)) } + if len(appPodSecurityContext) > 0 { + env = append(env, fmt.Sprintf("SHUFFLE_APP_POD_SECURITY_CONTEXT=%s", appPodSecurityContext)) + } + + if len(appContainerSecurityContext) > 0 { + env = append(env, fmt.Sprintf("SHUFFLE_APP_CONTAINER_SECURITY_CONTEXT=%s", appContainerSecurityContext)) + } + clientset, _, err := shuffle.GetKubernetesClient() if err != nil { log.Printf("[ERROR] Error getting kubernetes client:", err) @@ -1081,10 +1093,33 @@ func deployK8sWorker(image string, identifier string, env []string) error { "app.kubernetes.io/instance": identifier, } + // Parse security contexts from env + var podSecurityContext *corev1.PodSecurityContext + var containerSecurityContext *corev1.SecurityContext + + if len(workerPodSecurityContext) > 0 { + podSecurityContext = &corev1.PodSecurityContext{} + err = json.Unmarshal([]byte(workerPodSecurityContext), podSecurityContext) + if err != nil { + log.Printf("[ERROR] Failed to unmarshal worker pod security context: %v", err) + return fmt.Errorf("failed to unmarshal worker pod security context: %v", err) + } + } + + if len(workerContainerSecurityContext) > 0 { + containerSecurityContext = &corev1.SecurityContext{} + err = json.Unmarshal([]byte(workerContainerSecurityContext), containerSecurityContext) + if err != nil { + log.Printf("[ERROR] Failed to unmarshal worker container security context: %v", err) + return fmt.Errorf("failed to unmarshal worker container security context: %v", err) + } + } + containerAttachment := corev1.Container{ - Name: identifier, - Image: kubernetesImage, - Env: buildEnvVars(envMap), + Name: identifier, + Image: kubernetesImage, + Env: buildEnvVars(envMap), + SecurityContext: containerSecurityContext, //ImagePullPolicy: "Never", ImagePullPolicy: corev1.PullIfNotPresent, @@ -1201,6 +1236,7 @@ func deployK8sWorker(image string, identifier string, env []string) error { }, DNSPolicy: corev1.DNSClusterFirst, ServiceAccountName: workerServiceAccountName, + SecurityContext: podSecurityContext, }, }, }, @@ -1271,7 +1307,6 @@ func deployWorker(image string, identifier string, env []string, executionReques Resources: container.Resources{}, } - // This is just to test the mounting locally so // I can control from what source I'm mounting // the certs to. Default behaviour is: diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index a8f1c204..4cdacbb7 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -57,9 +57,13 @@ var logsDisabled = os.Getenv("SHUFFLE_LOGS_DISABLED") var cleanupEnv = strings.ToLower(os.Getenv("CLEANUP")) var swarmNetworkName = os.Getenv("SHUFFLE_SWARM_NETWORK_NAME") var dockerApiVersion = strings.ToLower(os.Getenv("DOCKER_API_VERSION")) -var appServiceAccountName = os.Getenv("SHUFFLE_APP_SERVICE_ACCOUNT_NAME") +// Kubernetes settings +var appServiceAccountName = os.Getenv("SHUFFLE_APP_SERVICE_ACCOUNT_NAME") +var appPodSecurityContext = os.Getenv("SHUFFLE_APP_POD_SECURITY_CONTEXT") +var appContainerSecurityContext = os.Getenv("SHUFFLE_APP_CONTAINER_SECURITY_CONTEXT") var kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE") + var executionCount int64 var baseimagename = os.Getenv("SHUFFLE_BASE_IMAGE_NAME") @@ -503,6 +507,28 @@ func deployk8sApp(image string, identifier string, env []string) error { "app.kubernetes.io/instance": name, } + // Parse security contexts from env + var podSecurityContext *corev1.PodSecurityContext + var containerSecurityContext *corev1.SecurityContext + + if len(appPodSecurityContext) > 0 { + podSecurityContext = &corev1.PodSecurityContext{} + err = json.Unmarshal([]byte(appPodSecurityContext), podSecurityContext) + if err != nil { + log.Printf("[ERROR] Failed to unmarshal app pod security context: %v", err) + return fmt.Errorf("failed to unmarshal app pod security context: %v", err) + } + } + + if len(appContainerSecurityContext) > 0 { + containerSecurityContext = &corev1.SecurityContext{} + err = json.Unmarshal([]byte(appContainerSecurityContext), containerSecurityContext) + if err != nil { + log.Printf("[ERROR] Failed to unmarshal app container security context: %v", err) + return fmt.Errorf("failed to unmarshal app container security context: %v", err) + } + } + // pod := &corev1.Pod{ // ObjectMeta: metav1.ObjectMeta{ // Name: podName, @@ -596,13 +622,15 @@ func deployk8sApp(image string, identifier string, env []string) error { Spec: corev1.PodSpec{ Containers: []corev1.Container{ { - Name: value, - Image: image, - Env: buildEnvVars(envMap), + Name: value, + Image: image, + Env: buildEnvVars(envMap), + SecurityContext: containerSecurityContext, }, }, DNSPolicy: corev1.DNSClusterFirst, ServiceAccountName: appServiceAccountName, + SecurityContext: podSecurityContext, }, }, }, @@ -917,7 +945,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] // Add more volume binds if possible if len(volumeBinds) > 0 { - // Only use mounts, not direct binds + // Only use mounts, not direct binds hostConfig.Binds = []string{} hostConfig.Mounts = []mount.Mount{} for _, bind := range volumeBinds { @@ -931,7 +959,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] sourceFolder := bindSplit[0] destinationFolder := bindSplit[1] - readOnly := false + readOnly := false if len(bindSplit) > 2 { mode := bindSplit[2] if mode == "ro" { @@ -940,9 +968,9 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] } builtMount := mount.Mount{ - Type: mount.TypeBind, - Source: sourceFolder, - Target: destinationFolder, + Type: mount.TypeBind, + Source: sourceFolder, + Target: destinationFolder, ReadOnly: readOnly, } @@ -1853,18 +1881,18 @@ func executionInit(workflowExecution shuffle.WorkflowExecution) error { } } - // Validates RERUN of single actions - // Identified by: + // Validates RERUN of single actions + // Identified by: // 1. Predefined result from previous exec // 2. Only ONE action // 3. Every predefined result having result.Action.Category == "rerun" /* - if len(workflowExecution.Workflow.Actions) == 1 && len(workflowExecution.Results) > 0 { - finished := shuffle.ValidateFinished(ctx, extra, workflowExecution) - if finished { - return nil + if len(workflowExecution.Workflow.Actions) == 1 && len(workflowExecution.Results) > 0 { + finished := shuffle.ValidateFinished(ctx, extra, workflowExecution) + if finished { + return nil + } } - } */ nextActions = append(nextActions, startAction) @@ -1954,7 +1982,6 @@ func executionInit(workflowExecution shuffle.WorkflowExecution) error { //log.Printf("Successfully downloaded and built %s", image) } - visited := []string{} executed := []string{} environments := []string{} @@ -3777,7 +3804,7 @@ func checkStandaloneRun() { if !strings.Contains(backendUrl, "http") { log.Printf("[ERROR] Backend URL should start with http:// or https://") return - + } // Format: @@ -3851,7 +3878,7 @@ func checkStandaloneRun() { continue } - // This is to handle reruns of SINGLE actions + // This is to handle reruns of SINGLE actions if result.Action.Category == "rerun" { newResults = append(newResults, result) continue @@ -3905,7 +3932,6 @@ func checkStandaloneRun() { log.Printf("\n\n\n[DEBUG] Finished resetting execution %s. Body: %s. Starting execution.\n\n\n", newresp.Status, string(body)) - } // Initial loop etc From e4a42128e7722abb9728a8681ef2b4fb8aa803b0 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> Date: Thu, 17 Apr 2025 12:06:59 +0200 Subject: [PATCH 04/47] feat(helm): allow to configure exposed app container port Signed-off-by: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> --- functions/kubernetes/charts/shuffle/README.md | 2 ++ .../shuffle/templates/orborus/orborus-dpl.yaml | 2 ++ .../shuffle-app/shuffle-app-network-policy.yaml | 13 +++++++------ .../kubernetes/charts/shuffle/values.schema.json | 5 +++++ functions/kubernetes/charts/shuffle/values.yaml | 4 ++++ 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/functions/kubernetes/charts/shuffle/README.md b/functions/kubernetes/charts/shuffle/README.md index ff3885d1..1faef45c 100644 --- a/functions/kubernetes/charts/shuffle/README.md +++ b/functions/kubernetes/charts/shuffle/README.md @@ -510,6 +510,7 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia | `app.networkPolicy.allowExternalEgress` | Allow the pod to access any range of port and all destinations. | `true` | | `app.networkPolicy.extraIngress` | Add extra ingress rules to the NetworkPolicy | `[]` | | `app.networkPolicy.extraEgress` | Add extra ingress rules to the NetworkPolicy (ignored if allowExternalEgress=true) | `[]` | +| `app.exposedContainerPort` | The port that shuffle app containers will listen on for new requests. | `80` | ### Traffic Exposure Parameters @@ -607,3 +608,4 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia ### Other Parameters + diff --git a/functions/kubernetes/charts/shuffle/templates/orborus/orborus-dpl.yaml b/functions/kubernetes/charts/shuffle/templates/orborus/orborus-dpl.yaml index a2d9c278..3e6616ef 100644 --- a/functions/kubernetes/charts/shuffle/templates/orborus/orborus-dpl.yaml +++ b/functions/kubernetes/charts/shuffle/templates/orborus/orborus-dpl.yaml @@ -88,6 +88,8 @@ spec: value: "true" - name: SHUFFLE_WORKER_SERVICE_ACCOUNT_NAME value: {{ include "shuffle.worker.serviceAccount.name" . }} + - name: SHUFFLE_APP_EXPOSED_PORT + value: {{ .Values.app.exposedContainerPort | quote }} - name: SHUFFLE_APP_SERVICE_ACCOUNT_NAME value: {{ include "shuffle.app.serviceAccount.name" . }} {{- if .Values.orborus.extraEnvVars }} diff --git a/functions/kubernetes/charts/shuffle/templates/shuffle-app/shuffle-app-network-policy.yaml b/functions/kubernetes/charts/shuffle/templates/shuffle-app/shuffle-app-network-policy.yaml index d4a24fe6..74600475 100644 --- a/functions/kubernetes/charts/shuffle/templates/shuffle-app/shuffle-app-network-policy.yaml +++ b/functions/kubernetes/charts/shuffle/templates/shuffle-app/shuffle-app-network-policy.yaml @@ -44,17 +44,18 @@ spec: {{- end }} {{- end }} ingress: - {{- if .Values.app.networkPolicy.allowExternal }} - - {} - {{- else }} - # Allow access from workers. Apps will typicaly use port 80/TCP, but this is not enforced. - - from: + - ports: + - port: {{ .Values.app.exposedContainerPort }} + protocol: TCP + {{- if not .Values.app.networkPolicy.allowExternal }} + # Allow traffic from workers + from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: {{ .Release.Namespace }} podSelector: matchLabels: {{ include "shuffle.worker.matchLabels" . | nindent 14 }} - {{- end }} + {{- end }} {{- if .Values.app.networkPolicy.extraIngress }} {{- include "common.tplvalues.render" ( dict "value" .Values.app.networkPolicy.extraIngress "context" $ ) | nindent 4 }} {{- end }} diff --git a/functions/kubernetes/charts/shuffle/values.schema.json b/functions/kubernetes/charts/shuffle/values.schema.json index b785b76e..c1b89282 100644 --- a/functions/kubernetes/charts/shuffle/values.schema.json +++ b/functions/kubernetes/charts/shuffle/values.schema.json @@ -2224,6 +2224,11 @@ "items": {} } } + }, + "exposedContainerPort": { + "type": "number", + "description": "The port that shuffle app containers will listen on for new requests. ", + "default": 80 } } }, diff --git a/functions/kubernetes/charts/shuffle/values.yaml b/functions/kubernetes/charts/shuffle/values.yaml index 507c2468..c63bfd22 100644 --- a/functions/kubernetes/charts/shuffle/values.yaml +++ b/functions/kubernetes/charts/shuffle/values.yaml @@ -1449,6 +1449,10 @@ app: ## extraEgress: [] + ## @param app.exposedContainerPort The port that shuffle app containers will listen on for new requests. + ## + exposedContainerPort: 80 + ## @section Traffic Exposure Parameters ## From ff2fc39c379224c70135ad79eb3564d5621008e6 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> Date: Fri, 25 Apr 2025 08:26:37 +0200 Subject: [PATCH 05/47] default backed update strategy to recreate Signed-off-by: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> --- functions/kubernetes/charts/shuffle/README.md | 2 +- functions/kubernetes/charts/shuffle/values.schema.json | 2 +- functions/kubernetes/charts/shuffle/values.yaml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/functions/kubernetes/charts/shuffle/README.md b/functions/kubernetes/charts/shuffle/README.md index ff3885d1..45988a4c 100644 --- a/functions/kubernetes/charts/shuffle/README.md +++ b/functions/kubernetes/charts/shuffle/README.md @@ -217,7 +217,7 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia | `backend.affinity` | Affinity for backend pods assignment | `{}` | | `backend.nodeSelector` | Node labels for backend pods assignment | `{}` | | `backend.tolerations` | Tolerations for backend pods assignment | `[]` | -| `backend.updateStrategy.type` | backend deployment strategy type | `RollingUpdate` | +| `backend.updateStrategy.type` | backend deployment strategy type | `Recreate` | | `backend.priorityClassName` | backend pods' priorityClassName | `""` | | `backend.topologySpreadConstraints` | Topology Spread Constraints for backend pod assignment spread across your cluster among failure-domains | `[]` | | `backend.schedulerName` | Name of the k8s scheduler (other than default) for backend pods | `""` | diff --git a/functions/kubernetes/charts/shuffle/values.schema.json b/functions/kubernetes/charts/shuffle/values.schema.json index b785b76e..529f484d 100644 --- a/functions/kubernetes/charts/shuffle/values.schema.json +++ b/functions/kubernetes/charts/shuffle/values.schema.json @@ -517,7 +517,7 @@ "type": { "type": "string", "description": "backend deployment strategy type", - "default": "RollingUpdate" + "default": "Recreate" } } }, diff --git a/functions/kubernetes/charts/shuffle/values.yaml b/functions/kubernetes/charts/shuffle/values.yaml index 507c2468..9723e96c 100644 --- a/functions/kubernetes/charts/shuffle/values.yaml +++ b/functions/kubernetes/charts/shuffle/values.yaml @@ -310,14 +310,14 @@ backend: ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ ## tolerations: [] - ## ONLY FOR DEPLOYMENTS: ## @param backend.updateStrategy.type backend deployment strategy type ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy ## updateStrategy: ## Can be set to RollingUpdate or Recreate + ## Backend uses ReadWriteOnce volumes by default, which is incompatible with RollingUpdate ## - type: RollingUpdate + type: Recreate ## @param backend.priorityClassName backend pods' priorityClassName ## priorityClassName: "" From 57287cc54bf2cffd95c5ade3cc9f0d5e58b09864 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> Date: Fri, 25 Apr 2025 08:27:12 +0200 Subject: [PATCH 06/47] allow to configure service labels for backend and frontend Signed-off-by: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> --- functions/kubernetes/charts/shuffle/README.md | 2 ++ .../templates/backend/backend-svc.yaml | 3 ++- .../templates/frontend/frontend-svc.yaml | 3 ++- .../charts/shuffle/values.schema.json | 20 +++++++++++++++++++ .../kubernetes/charts/shuffle/values.yaml | 16 +++++++++++++++ 5 files changed, 42 insertions(+), 2 deletions(-) diff --git a/functions/kubernetes/charts/shuffle/README.md b/functions/kubernetes/charts/shuffle/README.md index 45988a4c..f0c9df60 100644 --- a/functions/kubernetes/charts/shuffle/README.md +++ b/functions/kubernetes/charts/shuffle/README.md @@ -244,6 +244,7 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia | `backend.autoscaling.hpa.maxReplicas` | Maximum number of replicas | `""` | | `backend.autoscaling.hpa.targetCPU` | Target CPU utilization percentage | `""` | | `backend.autoscaling.hpa.targetMemory` | Target Memory utilization percentage | `""` | +| `backend.service.labels` | Extra labels for backend service | `{}` | | `backend.serviceAccount.create` | Specifies whether a ServiceAccount should be created | `true` | | `backend.serviceAccount.name` | The name of the ServiceAccount to use. | `""` | | `backend.serviceAccount.annotations` | Additional Service Account annotations (evaluated as a template) | `{}` | @@ -359,6 +360,7 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia | `frontend.autoscaling.hpa.maxReplicas` | Maximum number of replicas | `""` | | `frontend.autoscaling.hpa.targetCPU` | Target CPU utilization percentage | `""` | | `frontend.autoscaling.hpa.targetMemory` | Target Memory utilization percentage | `""` | +| `frontend.service.labels` | Extra labels for frontend service | `{}` | | `frontend.serviceAccount.create` | Specifies whether a ServiceAccount should be created | `true` | | `frontend.serviceAccount.name` | The name of the ServiceAccount to use. | `""` | | `frontend.serviceAccount.annotations` | Additional Service Account annotations (evaluated as a template) | `{}` | diff --git a/functions/kubernetes/charts/shuffle/templates/backend/backend-svc.yaml b/functions/kubernetes/charts/shuffle/templates/backend/backend-svc.yaml index 18328899..d85382e3 100644 --- a/functions/kubernetes/charts/shuffle/templates/backend/backend-svc.yaml +++ b/functions/kubernetes/charts/shuffle/templates/backend/backend-svc.yaml @@ -3,7 +3,8 @@ kind: Service metadata: name: {{ template "shuffle.backend.name" . }} namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "shuffle.backend.labels" (dict "customLabels" .Values.commonLabels "context" $) | nindent 4 }} + {{- $serviceLabels := include "common.tplvalues.merge" (dict "values" (list .Values.backend.service.labels .Values.commonLabels) "context" .) }} + labels: {{- include "shuffle.backend.labels" (dict "customLabels" $serviceLabels "context" $) | nindent 4 }} {{- if .Values.commonAnnotations }} annotations: {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} {{- end }} diff --git a/functions/kubernetes/charts/shuffle/templates/frontend/frontend-svc.yaml b/functions/kubernetes/charts/shuffle/templates/frontend/frontend-svc.yaml index 76851c0a..37b8140d 100644 --- a/functions/kubernetes/charts/shuffle/templates/frontend/frontend-svc.yaml +++ b/functions/kubernetes/charts/shuffle/templates/frontend/frontend-svc.yaml @@ -3,7 +3,8 @@ kind: Service metadata: name: {{ template "shuffle.frontend.name" . }} namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "shuffle.frontend.labels" (dict "customLabels" .Values.commonLabels "context" $) | nindent 4 }} + {{- $serviceLabels := include "common.tplvalues.merge" (dict "values" (list .Values.frontend.service.labels .Values.commonLabels) "context" .) }} + labels: {{- include "shuffle.frontend.labels" (dict "customLabels" $serviceLabels "context" $) | nindent 4 }} {{- if .Values.commonAnnotations }} annotations: {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} {{- end }} diff --git a/functions/kubernetes/charts/shuffle/values.schema.json b/functions/kubernetes/charts/shuffle/values.schema.json index 529f484d..a46c274d 100644 --- a/functions/kubernetes/charts/shuffle/values.schema.json +++ b/functions/kubernetes/charts/shuffle/values.schema.json @@ -683,6 +683,16 @@ } } }, + "service": { + "type": "object", + "properties": { + "labels": { + "type": "object", + "description": "Extra labels for backend service", + "default": {} + } + } + }, "serviceAccount": { "type": "object", "properties": { @@ -1362,6 +1372,16 @@ } } }, + "service": { + "type": "object", + "properties": { + "labels": { + "type": "object", + "description": "Extra labels for frontend service", + "default": {} + } + } + }, "serviceAccount": { "type": "object", "properties": { diff --git a/functions/kubernetes/charts/shuffle/values.yaml b/functions/kubernetes/charts/shuffle/values.yaml index 9723e96c..6dfdcd97 100644 --- a/functions/kubernetes/charts/shuffle/values.yaml +++ b/functions/kubernetes/charts/shuffle/values.yaml @@ -421,6 +421,14 @@ backend: targetCPU: "" targetMemory: "" + ## Service configuration + ## + service: + ## @param backend.service.labels Extra labels for backend service + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + ## + labels: {} + ## ServiceAccount configuration ## serviceAccount: @@ -870,6 +878,14 @@ frontend: targetCPU: "" targetMemory: "" + ## Service configuration + ## + service: + ## @param frontend.service.labels Extra labels for frontend service + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + ## + labels: {} + ## ServiceAccount configuration ## serviceAccount: From 39cfdb84cec8a380f4ca3a071fc32a7e56a2567b Mon Sep 17 00:00:00 2001 From: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> Date: Fri, 25 Apr 2025 08:27:18 +0200 Subject: [PATCH 07/47] add appProtocol Signed-off-by: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> --- .../charts/shuffle/templates/backend/backend-svc.yaml | 1 + .../charts/shuffle/templates/frontend/frontend-svc.yaml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/functions/kubernetes/charts/shuffle/templates/backend/backend-svc.yaml b/functions/kubernetes/charts/shuffle/templates/backend/backend-svc.yaml index d85382e3..990f9410 100644 --- a/functions/kubernetes/charts/shuffle/templates/backend/backend-svc.yaml +++ b/functions/kubernetes/charts/shuffle/templates/backend/backend-svc.yaml @@ -15,5 +15,6 @@ spec: port: {{ .Values.backend.containerPorts.http }} targetPort: http protocol: TCP + appProtocol: http {{- $podLabels := include "common.tplvalues.merge" (dict "values" (list .Values.backend.podLabels .Values.commonLabels) "context" .) }} selector: {{- include "shuffle.backend.matchLabels" (dict "customLabels" $podLabels "context" $) | nindent 4 }} diff --git a/functions/kubernetes/charts/shuffle/templates/frontend/frontend-svc.yaml b/functions/kubernetes/charts/shuffle/templates/frontend/frontend-svc.yaml index 37b8140d..de2db9fb 100644 --- a/functions/kubernetes/charts/shuffle/templates/frontend/frontend-svc.yaml +++ b/functions/kubernetes/charts/shuffle/templates/frontend/frontend-svc.yaml @@ -15,11 +15,13 @@ spec: port: {{ .Values.frontend.containerPorts.http }} targetPort: http protocol: TCP + appProtocol: http {{- if .Values.frontend.containerPorts.https }} - name: https port: {{ .Values.frontend.containerPorts.https }} targetPort: https protocol: TCP + appProtocol: https {{- end }} {{- $podLabels := include "common.tplvalues.merge" (dict "values" (list .Values.frontend.podLabels .Values.commonLabels) "context" .) }} selector: {{- include "shuffle.frontend.matchLabels" (dict "customLabels" $podLabels "context" $) | nindent 4 }} From 99c01e518dad19891188239de9eed2603b5178ce Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 12 May 2025 13:15:43 +0200 Subject: [PATCH 08/47] Fixed a problem where workflows can't be built --- backend/go-app/go.mod | 2 +- backend/go-app/walkoff.go | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 8e686705..ea02517b 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -22,7 +22,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.39 + github.com/shuffle/shuffle-shared v0.8.50 golang.org/x/crypto v0.36.0 google.golang.org/api v0.228.0 google.golang.org/grpc v1.71.1 diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 14c5ac28..8357de3e 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -3039,7 +3039,13 @@ func executeSingleAction(resp http.ResponseWriter, request *http.Request) { shouldRerun = true } - workflowExecution, err := shuffle.PrepareSingleAction(ctx, user, fileId, body, runValidationAction) + decisionId := "" + decision, decisionOk := query["decision_id"] + if decisionOk && len(decision) > 0 { + decisionId = decision[0] + } + + workflowExecution, err := shuffle.PrepareSingleAction(ctx, user, fileId, body, runValidationAction, decisionId) debugUrl := fmt.Sprintf("/workflows/%s?execution_id=%s", workflowExecution.Workflow.ID, workflowExecution.ExecutionId) resp.Header().Add("X-Debug-Url", debugUrl) @@ -3101,7 +3107,12 @@ func executeSingleAction(resp http.ResponseWriter, request *http.Request) { return } - returnBody := shuffle.HandleRetValidation(ctx, workflowExecution, 1) + actionId := "" + if len(workflowExecution.Workflow.Actions) == 1 { + actionId = workflowExecution.Workflow.Actions[0].ID + } + + returnBody := shuffle.HandleRetValidation(ctx, workflowExecution, 1, actionId) returnBytes, err := json.Marshal(returnBody) if err != nil { log.Printf("[ERROR] Failed to marshal retStruct in single execution: %s", err) From ef8e01b73fb7337bc36ef6e34f2f999044c9ce08 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 12 May 2025 13:20:58 +0200 Subject: [PATCH 09/47] Rebuild with right permissions --- backend/go-app/go.sum | 4 ++-- backend/go-app/main.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index a3382d6a..ca216f74 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -341,8 +341,8 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fc github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shuffle/shuffle-shared v0.8.39 h1:ylRj+2xGIOPQfpawf45udnQzKLtTP9JFEFyREc2GJXM= -github.com/shuffle/shuffle-shared v0.8.39/go.mod h1:z+ISGBgNINmZvWNrtGTc51yVG+pMkpBFu9ZLVlTyuag= +github.com/shuffle/shuffle-shared v0.8.50 h1:Sy6o7Nrcd3QG+m28775STKS3uOfaXDzDcctTuaYfdYQ= +github.com/shuffle/shuffle-shared v0.8.50/go.mod h1:z+ISGBgNINmZvWNrtGTc51yVG+pMkpBFu9ZLVlTyuag= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 68fc4e88..fbd2a251 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -5138,7 +5138,7 @@ func initHandlers() { r.HandleFunc("/api/v1/apps/{key}/execute", executeSingleAction).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/apps/{key}/run", executeSingleAction).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/apps/categories", shuffle.GetActiveCategories).Methods("GET", "OPTIONS") - r.HandleFunc("/api/v1/apps/categories/run", shuffle.RunCategoryAction).Methods("POST", "OPTIONS") + //r.HandleFunc("/api/v1/apps/categories/run", shuffle.RunCategoryAction).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/apps/upload", handleAppZipUpload).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/apps/{appId}/activate", activateWorkflowAppDocker).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/apps/{appId}/deactivate", activateWorkflowAppDocker).Methods("GET", "OPTIONS") From b4ef1fa50c3dc54272d1679f5e6b75b7bc115e6c Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 14 May 2025 13:37:32 +0200 Subject: [PATCH 10/47] Added an extra error log for looking into https://github.com/Shuffle/Shuffle/issues/1449 --- functions/onprem/worker/worker.go | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index a8f1c204..840b70ea 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -74,6 +74,7 @@ var appsInitialized = false var hostname string var maxReplicas = uint64(12) +var debug bool /* var environments []string @@ -2548,11 +2549,16 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl } if setExecution || workflowExecution.Status == "FINISHED" || workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" { - log.Printf("[DEBUG][%s] Running setexec with status %s and %d/%d results", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions)) + if debug { + log.Printf("[DEBUG][%s] Running setexec with status %s and %d/%d results", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions)) + } + //result(s)", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results)) err = setWorkflowExecution(ctx, *workflowExecution, dbSave) if err != nil { - resp.WriteHeader(401) + log.Printf("[ERROR][%s] Failed setting execution: %s", workflowExecution.ExecutionId, err) + + resp.WriteHeader(400) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed setting workflowexecution actionresult: %s"}`, err))) return } @@ -2561,7 +2567,10 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" { finished := shuffle.ValidateFinished(ctx, -1, *workflowExecution) if !finished { - log.Printf("[DEBUG][%s] Handling next node since it's not finished!", workflowExecution.ExecutionId) + if debug { + log.Printf("[DEBUG][%s] Handling next node since it's not finished!", workflowExecution.ExecutionId) + } + handleExecutionResult(*workflowExecution) } else { shutdownData, err := json.Marshal(workflowExecution) @@ -3519,7 +3528,9 @@ func sendAppRequest(ctx context.Context, incomingUrl, appName string, port int, log.Printf("[ERROR] Failed reading app request body body: %s", err) return err } else { - log.Printf("[DEBUG][%s] NEWRESP (from app): %s", workflowExecution.ExecutionId, string(body)) + if debug { + log.Printf("[DEBUG][%s] NEWRESP (from app): %s", workflowExecution.ExecutionId, string(body)) + } } return nil @@ -3912,6 +3923,10 @@ func checkStandaloneRun() { func main() { checkStandaloneRun() + if os.Getenv("DEBUG") == "true" { + debug = true + } + /*** STARTREMOVE ***/ if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" { logsDisabled = "true" From e9d934a6d97f56f69d7e182f5ee7efa435bd3442 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> Date: Wed, 14 May 2025 15:10:11 +0200 Subject: [PATCH 11/47] helm: set backend_url for workflow executions to cluster-internal address Signed-off-by: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> --- .../shuffle/templates/backend/backend-cm-env.yaml | 6 ++++-- .../shuffle/templates/orborus/orborus-cm-env.yaml | 2 +- .../shuffle-app/shuffle-app-network-policy.yaml | 10 ++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/functions/kubernetes/charts/shuffle/templates/backend/backend-cm-env.yaml b/functions/kubernetes/charts/shuffle/templates/backend/backend-cm-env.yaml index e7138dc0..dbbdff94 100644 --- a/functions/kubernetes/charts/shuffle/templates/backend/backend-cm-env.yaml +++ b/functions/kubernetes/charts/shuffle/templates/backend/backend-cm-env.yaml @@ -8,18 +8,20 @@ metadata: annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} {{- end }} data: - BACKEND_PORT: "5001" + BACKEND_PORT: "{{ .Values.backend.containerPorts.http }}" {{- if .Values.shuffle.baseUrl }} BASE_URL: "{{ .Values.shuffle.baseUrl }}" SSO_REDIRECT_URL: "{{ .Values.shuffle.baseUrl }}" {{- else }} - BASE_URL: "http://{{ include "shuffle.backend.name" . }}:5001" + BASE_URL: "http://{{ include "shuffle.backend.name" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.backend.containerPorts.http }}" {{- end }} ORG_ID: "{{ .Values.shuffle.org }}" SHUFFLE_APP_DOWNLOAD_LOCATION: "{{ .Values.backend.apps.downloadLocation }}" SHUFFLE_DOWNLOAD_AUTH_BRANCH: "{{ .Values.backend.apps.downloadBranch }}" SHUFFLE_APP_FORCE_UPDATE: "{{ .Values.backend.apps.forceUpdate }}" SHUFFLE_CHAT_DISABLED: "true" + # Sets backend_url parameter for workflow execution to the cluster-internal shuffle-backend address + SHUFFLE_CLOUDRUN_URL: "http://{{ include "shuffle.backend.name" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.backend.containerPorts.http }}" SHUFFLE_OPENSEARCH_URL: {{ include "common.tplvalues.render" (dict "value" .Values.backend.openSearch.url "context" $) }} SHUFFLE_OPENSEARCH_USERNAME: "{{ .Values.backend.openSearch.username }}" SHUFFLE_OPENSEARCH_CERTIFICATE_FILE: "{{ .Values.backend.openSearch.certificateFile }}" diff --git a/functions/kubernetes/charts/shuffle/templates/orborus/orborus-cm-env.yaml b/functions/kubernetes/charts/shuffle/templates/orborus/orborus-cm-env.yaml index 57b020a7..504b674d 100644 --- a/functions/kubernetes/charts/shuffle/templates/orborus/orborus-cm-env.yaml +++ b/functions/kubernetes/charts/shuffle/templates/orborus/orborus-cm-env.yaml @@ -11,7 +11,7 @@ data: ENVIRONMENT_NAME: "{{ .Values.shuffle.org }}" ORG_ID: "{{ .Values.shuffle.org }}" TZ: "{{ .Values.shuffle.timezone }}" - BASE_URL: "http://{{ include "shuffle.backend.name" . }}.{{ .Release.Namespace }}.svc.cluster.local:5001" + BASE_URL: "http://{{ include "shuffle.backend.name" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.backend.containerPorts.http }}" KUBERNETES_NAMESPACE: "{{ .Release.Namespace }}" KUBERNETES_SERVICE_ACCOUNT: {{ include "shuffle.orborus.serviceAccount.name" . }} SHUFFLE_WORKER_IMAGE: "{{ include "shuffle.worker.image" . }}" diff --git a/functions/kubernetes/charts/shuffle/templates/shuffle-app/shuffle-app-network-policy.yaml b/functions/kubernetes/charts/shuffle/templates/shuffle-app/shuffle-app-network-policy.yaml index d4a24fe6..5303123d 100644 --- a/functions/kubernetes/charts/shuffle/templates/shuffle-app/shuffle-app-network-policy.yaml +++ b/functions/kubernetes/charts/shuffle/templates/shuffle-app/shuffle-app-network-policy.yaml @@ -29,6 +29,16 @@ spec: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: kube-system + # Allow access to backend + - ports: + - port: {{ .Values.backend.containerPorts.http }} + protocol: TCP + to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: {{ .Release.Namespace }} + podSelector: + matchLabels: {{ include "shuffle.backend.matchLabels" . | nindent 14 }} # Allow access to workers - ports: - port: 33333 From 8db79e24f55854adea160bc57f965588951bda47 Mon Sep 17 00:00:00 2001 From: Frikky Date: Sun, 18 May 2025 22:40:15 +0200 Subject: [PATCH 12/47] Added more of the new relevant paths --- frontend/src/App.jsx | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 7fa7c847..2192be04 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -791,6 +791,40 @@ const App = (message, props) => { /> } /> + + } + /> + + } + /> { /> } /> + + + } + /> + Date: Mon, 19 May 2025 00:04:37 +0200 Subject: [PATCH 13/47] Lightmode/Darkmode merge including many fixes since 2.0.2 --- backend/go-app/go.mod | 2 +- frontend/public/images/icons/shuffleLogo.svg | 5 + frontend/src/components/AdminNavBar.jsx | 282 +++- frontend/src/components/ApiExplorer.jsx | 82 +- frontend/src/components/AppAuthTab.jsx | 120 +- frontend/src/components/AppCreationModal.jsx | 38 +- frontend/src/components/AppModal.jsx | 49 +- frontend/src/components/AppSearchButtons.jsx | 19 +- frontend/src/components/AppStats.jsx | 11 +- frontend/src/components/Appsearch.jsx | 15 +- frontend/src/components/Billing.jsx | 649 ++++++++- frontend/src/components/BillingStats.jsx | 50 +- frontend/src/components/CacheView.jsx | 101 +- frontend/src/components/ChatBot.jsx | 846 ++++++++++++ frontend/src/components/CloudSyncTab.jsx | 49 +- frontend/src/components/ConfigureWorkflow.jsx | 10 +- frontend/src/components/EditOrgTab.jsx | 18 +- frontend/src/components/EditWorkflow.jsx | 49 +- frontend/src/components/EnvironmentTab.jsx | 113 +- frontend/src/components/Files.jsx | 154 ++- frontend/src/components/LeftSideBar.jsx | 848 ++++++------ frontend/src/components/LicencePopup.jsx | 36 +- frontend/src/components/Navbar.jsx | 166 ++- frontend/src/components/Oauth2Auth.jsx | 13 +- frontend/src/components/OrgHeaderNew.jsx | 28 +- .../src/components/OrgHeaderexpandedNew.jsx | 96 +- frontend/src/components/OrganizationTab.jsx | 72 +- frontend/src/components/ParsedAction.jsx | 181 ++- frontend/src/components/Priorities.jsx | 125 +- frontend/src/components/Priority.jsx | 22 +- frontend/src/components/RecentWorkflow.jsx | 13 +- frontend/src/components/RenderCytoscape.jsx | 10 +- frontend/src/components/RuntimeDebugger.jsx | 61 +- frontend/src/components/SchedulesTab.jsx | 117 +- frontend/src/components/SearchData.jsx | 50 +- frontend/src/components/Searchfield.jsx | 7 +- .../src/components/ShuffleCodeEditor1.jsx | 48 +- frontend/src/components/TenantsTab.jsx | 131 +- frontend/src/components/UserManagmentTab.jsx | 126 +- .../src/components/WorkflowTemplatePopup2.jsx | 24 +- frontend/src/components/ssoTab.jsx | 141 +- frontend/src/context/ContextApi.jsx | 74 +- frontend/src/defaultCytoscapeStyle.jsx | 1181 +++++++++-------- frontend/src/theme.jsx | 296 ++++- frontend/src/views/Admin2.jsx | 15 +- frontend/src/views/AngularWorkflow.jsx | 1147 +++++++++------- frontend/src/views/ApiExplorerWrapper.jsx | 9 +- frontend/src/views/AppCreator.jsx | 312 +++-- frontend/src/views/AppExplorer.jsx | 156 ++- frontend/src/views/Apps2.jsx | 129 +- frontend/src/views/Docs.jsx | 116 +- frontend/src/views/LoginPage.jsx | 2 +- frontend/src/views/RunWorkflow.jsx | 8 +- frontend/src/views/SetAuthentication.jsx | 7 +- frontend/src/views/SettingsPage.jsx | 49 +- frontend/src/views/UpdateAuthentication.jsx | 7 +- frontend/src/views/Usecases2.jsx | 30 +- frontend/src/views/Workflows.jsx | 110 ++ frontend/src/views/Workflows2.jsx | 267 ++-- 59 files changed, 5931 insertions(+), 2961 deletions(-) create mode 100755 frontend/public/images/icons/shuffleLogo.svg create mode 100644 frontend/src/components/ChatBot.jsx diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index ea02517b..6e354ef6 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -23,7 +23,7 @@ require ( github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 github.com/shuffle/shuffle-shared v0.8.50 - golang.org/x/crypto v0.36.0 + golang.org/x/crypto v0.37.0 google.golang.org/api v0.228.0 google.golang.org/grpc v1.71.1 gopkg.in/yaml.v3 v3.0.1 diff --git a/frontend/public/images/icons/shuffleLogo.svg b/frontend/public/images/icons/shuffleLogo.svg new file mode 100755 index 00000000..1024dd6a --- /dev/null +++ b/frontend/public/images/icons/shuffleLogo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/components/AdminNavBar.jsx b/frontend/src/components/AdminNavBar.jsx index e0faec6e..a37b8c15 100644 --- a/frontend/src/components/AdminNavBar.jsx +++ b/frontend/src/components/AdminNavBar.jsx @@ -19,17 +19,97 @@ import { FmdGoodOutlined as FmdGoodOutlinedIcon, GroupOutlined as GroupOutlinedIcon } from '@mui/icons-material'; -import theme from '../theme.jsx'; -import { Button, Tooltip } from '@mui/material'; +import theme, { getTheme } from '../theme.jsx'; +import { Button, Skeleton, Tooltip } from '@mui/material'; import { Index } from 'react-instantsearch-dom'; import { Context } from '../context/ContextApi.jsx'; +import { toast } from 'react-toastify'; const AdminNavBar = (props) => { const location = useLocation(); - const { globalUrl, userdata, isCloud, isLoaded,removeCookie, handleStatusChange, selectedStatus, setSelectedStatus, handleEditOrg, serverside, notifications, handleGetOrg, orgId, checkLogin, setNotifications, stripeKey, setSelectedOrganization, selectedOrganization } = props; + const { globalUrl, userdata, isCloud,isOrgLoaded, isLoaded,removeCookie, handleStatusChange, selectedStatus, setSelectedStatus, handleEditOrg, serverside, notifications, handleGetOrg, orgId, checkLogin, setNotifications, stripeKey, setSelectedOrganization, selectedOrganization } = props; const [selectedItem, setSelectedItem] = useState("Organization"); const [isSelectedFiles, setIsSelectedFiles] = useState(true); const [isSelectedDataStore, setIsSelectedDataStore] = useState(true); + const [isIntegrationPartner, setIsIntegrationPartner] = useState(false); + const [isChildOrg, setIsChildOrg] = useState(false); + const [isGlobalUser, setIsGlobalUser] = useState(false); + const [visibleItems, setVisibleItems] = useState([]); + const [isUserDataLoaded, setIsUserDataLoaded] = useState(false); + + const items = [ + { iconSrc: , alt: "Organization Icon", text: "Organization", component: OrganizationTab, props: { isIntegrationPartner, isChildOrg, isGlobalUser, globalUrl,removeCookie, selectedStatus, isLoaded, setSelectedStatus, handleStatusChange, handleEditOrg, handleGetOrg, userdata, isCloud, serverside, notifications, checkLogin, setNotifications, stripeKey, setSelectedOrganization, selectedOrganization } }, + { iconSrc: , alt: "Users Icon", text: "Users", component: UserManagmentTab, props: { globalUrl, userdata, serverside, isCloud, selectedOrganization, setSelectedOrganization, handleEditOrg } }, + { iconSrc: , alt: "App Auth Icon", text: "App_auth", component: AppAuthTab, props: { globalUrl, userdata, isCloud, selectedOrganization } }, + { iconSrc: , alt: "Datastore Icon", text: "Datastore", component: CacheView, props: { globalUrl, userdata, selectedOrganization, serverside, isSelectedDataStore, orgId , isCloud} }, + { iconSrc: , alt: "Files Icon", text: "Files", component: Files, props: { isCloud, globalUrl, userdata, serverside, selectedOrganization, isSelectedFiles } }, + { iconSrc: , alt: "Trigger Icon", text: "Triggers", component: SchedulesTab, props: { globalUrl, userdata, isCloud, serverside } }, + { iconSrc: , alt: "Environments Icon", text: "Locations", component: EnvironmentTab, props: { globalUrl, userdata, isCloud, selectedOrganization } }, + { iconSrc: , alt: "Tenants Icon", text: "Tenants", component: TenantsTab, props: {isCloud, globalUrl, userdata, serverside, selectedOrganization, setSelectedOrganization, checkLogin } } + ]; + + + useEffect(() => { + if (userdata && userdata?.active_org?.id?.length > 0) { + setIsUserDataLoaded(true); + } + }, [userdata]); + + + + const { themeMode, brandColor } = React.useContext(Context); + const theme = getTheme(themeMode, brandColor); + + const HandlePartnerChange = () => { + if (userdata?.id?.length > 0) { + const isIntegrationPartner = userdata?.org_status?.includes("integration_partner") || false; + setIsIntegrationPartner(isIntegrationPartner); + const isChildOrg = userdata?.org_status?.includes("sub_org") || false; + setIsChildOrg(isChildOrg); + const isGlobalUser = userdata?.active_org?.branding?.global_user || false; + setIsGlobalUser(isGlobalUser); + } else { + setIsIntegrationPartner(false); + setIsChildOrg(false); + setIsGlobalUser(false); + } + } + + useEffect(() => { + if (userdata && userdata?.id?.length > 0) { + HandlePartnerChange(); + } + }, [userdata]); + + const HandleVisibleTabs = () => { + if (userdata?.id?.length > 0) { + if (userdata?.active_org?.role === "admin" || userdata?.support) { + setVisibleItems(items); + }else { + const filteredItems = items.filter(item => item.text !== "Users" && item.text !== "Files" && item.text !== "Datastore" && item.text !== "Triggers" && item.text !== "Locations"); + setVisibleItems(filteredItems); + } + } + } + + useEffect(() => { + if (isIntegrationPartner && isChildOrg && !isGlobalUser) { + // Filter out Users and Tenants tabs + if (userdata?.active_org?.role === "admin" || userdata?.support) { + const filteredItems = items.filter(item => + item.text !== "Users" && item.text !== "Tenants" + ); + setVisibleItems(filteredItems); + }else { + const filteredItems = items.filter(item => + item.text !== "Users" && item.text !== "Tenants" && item.text !== "Files" && item.text !== "Datastore" && item.text !== "Triggers" && item.text !== "Locations" + ); + setVisibleItems(filteredItems); + } + } else { + HandleVisibleTabs(); + } + }, [isIntegrationPartner, isChildOrg, isGlobalUser, selectedOrganization, userdata]); const navigate = useNavigate(); @@ -52,18 +132,6 @@ const AdminNavBar = (props) => { setSelectedItem("Organization"); } }, [location.search]); - - - const items = [ - { iconSrc: , alt: "Organization Icon", text: "Organization", component: OrganizationTab, props: { globalUrl,removeCookie, selectedStatus, isLoaded, setSelectedStatus, handleStatusChange, handleEditOrg, handleGetOrg, userdata, isCloud, serverside, notifications, checkLogin, setNotifications, stripeKey, setSelectedOrganization, selectedOrganization } }, - { iconSrc: , alt: "Users Icon", text: "Users", component: UserManagmentTab, props: { globalUrl, userdata, serverside, isCloud, selectedOrganization, setSelectedOrganization, handleEditOrg } }, - { iconSrc: , alt: "App Auth Icon", text: "App_auth", component: AppAuthTab, props: { globalUrl, userdata, isCloud, selectedOrganization } }, - { iconSrc: , alt: "Datastore Icon", text: "Datastore", component: CacheView, props: { globalUrl, userdata, selectedOrganization, serverside, isSelectedDataStore, orgId , isCloud} }, - { iconSrc: , alt: "Files Icon", text: "Files", component: Files, props: { isCloud, globalUrl, userdata, serverside, selectedOrganization, isSelectedFiles } }, - { iconSrc: , alt: "Trigger Icon", text: "Triggers", component: SchedulesTab, props: { globalUrl, userdata, isCloud, serverside } }, - { iconSrc: , alt: "Environments Icon", text: "Locations", component: EnvironmentTab, props: { globalUrl, userdata, isCloud, selectedOrganization } }, - { iconSrc: , alt: "Tenants Icon", text: "Tenants", component: TenantsTab, props: {isCloud, globalUrl, userdata, serverside, selectedOrganization, setSelectedOrganization, checkLogin } } - ]; const setConfig = (newValue) => { setSelectedItem(newValue); @@ -76,12 +144,65 @@ const AdminNavBar = (props) => { } }; + useEffect(() => { + if (isIntegrationPartner && isChildOrg && !isGlobalUser && isOrgLoaded && isUserDataLoaded) { + const queryParams = new URLSearchParams(location.search); + const tabName = queryParams?.get('admin_tab')?.toLowerCase(); + if (tabName === "sso" || tabName === "branding") { + toast.info("You are not allowed to access this tab. Please contact your admin for more information. Redirecting to Organization Configuration tab."); + setTimeout(() => { + setSelectedItem("Organization"); + navigate(`?admin_tab=org_config`, { replace: true }); + window.location.reload(); + } + , 3000); + } + + const params = new URLSearchParams(location.search); + const tab = params?.get('tab')?.toLowerCase(); + if (tab === "users" || tab === "tenants") { + toast.info("You are not allowed to access this tab. Please contact your admin for more information. Redirecting to Organization Configuration tab."); + setTimeout(() => { + setSelectedItem("Organization"); + navigate(`?admin_tab=org_config`, { replace: true }); + window.location.reload(); + } + , 3000); + } + } else if (userdata && isOrgLoaded && isUserDataLoaded && userdata?.active_org?.role !== "admin" && !userdata?.support) { + const queryParams = new URLSearchParams(location.search); + const tabName = queryParams?.get('admin_tab')?.toLowerCase(); + if (tabName === "sso") { + toast.info("You are not allowed to access this tab. Please contact your admin for more information. Redirecting to Organization Configuration tab."); + setTimeout(() => { + setSelectedItem("Organization"); + navigate(`?admin_tab=org_config`, { replace: true }); + window.location.reload(); + } + , 3000); + } + + const params = new URLSearchParams(location.search); + const tab = params?.get('tab')?.toLowerCase(); + if (tab === "users" || tab === "locations" || tab === "environments" || tab === "files" || tab === "datastore" || tab === "triggers") { + toast.info("You are not allowed to access this tab. Please contact your admin for more information. Redirecting to Organization Configuration tab."); + setTimeout(() => { + setSelectedItem("Organization"); + navigate(`?admin_tab=org_config`, { replace: true }); + window.location.reload(); + } + , 3000); + } + } + }, [isIntegrationPartner, isChildOrg, isGlobalUser, location.search, userdata, isOrgLoaded, isUserDataLoaded]); + + const renderComponent = () => { - const selectedItemData = items.find(item => item.text === selectedItem); + const selectedItemData = visibleItems.find(item => item.text === selectedItem); if (!selectedItemData) { setSelectedItem("Organization"); // If no tab is specified, default to "Organization" tab - return ; + return ; }; const ComponentToRender = selectedItemData.component; @@ -97,15 +218,16 @@ const AdminNavBar = (props) => { : selectedOrganization?.image; return ( + !isOrgLoaded && !isUserDataLoaded ? :
-