diff --git a/CHANGES.rst b/CHANGES.rst index 538446fc..1871c96c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,13 @@ Changes ======= +Version 0.9.1 (UNRELEASED) +-------------------------- + +- Administrators: + - Adds new configuration option ``interactive_sessions.maximum_inactivity_period`` to set a limit in days for the maximum inactivity period of interactive sessions after which they will be closed. + - Adds new configuration option ``interactive_sessions.cronjob_schedule`` to set how often interactive session cleanup should be performed. + Version 0.9.0 (2023-01-26) -------------------------- diff --git a/helm/reana/README.md b/helm/reana/README.md index 7a1bd25f..49bb3c5a 100644 --- a/helm/reana/README.md +++ b/helm/reana/README.md @@ -121,3 +121,5 @@ This Helm automatically prefixes all names using the release name to avoid colli | `workspaces.retention_rules.maximum_period` | Set a default period in days for workspace retention rules. Users will not be able to specify a longer period to retain the workspace files. After this period the workspace will be cleared. To disable the period and allow files to be kept forever, use value "forever". | forever | | `workspaces.retention_rules.cronjob_schedule` | Cron format string describing how often pending retention rules should be applied. | "0 2 * * *" | | `workspaces.paths` | List of additional workspace paths as strings. Each mount string is composed by a key `hostPath`(path to the directory to be mounted from the Kubernetes nodes) and a cluster_pod_mountpath (path inside the cluster containers where the `mountPath` will be mounted) e.g. `hostPath:mountPath`. The first value listed will be the default workspace root path. Any POSIX filesystem mounted on cluster nodes is supported | None | +| `interactive_sessions.cronjob_schedule` | Cron format string describing how often interactive session cleanup should be performed. | "0 3 * * *" | +| `interactive_sessions.maximum_inactivity_period` | Set a limit in days for the maximum inactivity period of interactive sessions. After this period interactive sessions will be automatically closed. To disable autoclosure and allow interactive sessions to run forever, use value "forever". | forever | \ No newline at end of file diff --git a/helm/reana/templates/cronjobs.yaml b/helm/reana/templates/cronjobs.yaml index 8a45de0a..c9158bba 100644 --- a/helm/reana/templates/cronjobs.yaml +++ b/helm/reana/templates/cronjobs.yaml @@ -257,3 +257,82 @@ spec: - name: reana-code hostPath: path: /code/reana-server +--- +{{- if not (contains "forever" (.Values.interactive_sessions.maximum_inactivity_period | quote)) }} +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ include "reana.prefix" . }}-interactive-session-cleanup + namespace: {{ .Release.Namespace }} +spec: + schedule: {{ .Values.interactive_sessions.cronjob_schedule | quote }} + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 1 + failedJobsHistoryLimit: 1 + {{- if .Values.maintenance.enabled }} + suspend: true + {{- end }} + jobTemplate: + spec: + template: + spec: + serviceAccountName: {{ include "reana.prefixed_infrastructure_svaccount_name" . }} + containers: + - name: {{ include "reana.prefix" . }}-interactive-session-cleanup + image: {{ .Values.components.reana_server.image }} + command: + - '/bin/sh' + - '-c' + args: + - 'flask reana-admin interactive-session-cleanup -d {{ .Values.interactive_sessions.maximum_inactivity_period }}' + {{- if .Values.debug.enabled }} + tty: true + stdin: true + {{- end }} + env: + {{- if .Values.reana_hostname }} + - name: REANA_HOSTNAME + value: {{ .Values.reana_hostname }} + {{- end }} + {{- range $key, $value := .Values.db_env_config }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- if .Values.debug.enabled }} + - name: FLASK_ENV + value: "development" + {{- else }} + - name: REANA_DB_USERNAME + valueFrom: + secretKeyRef: + name: {{ include "reana.prefix" . }}-db-secrets + key: user + - name: REANA_DB_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "reana.prefix" . }}-db-secrets + key: password + {{- end }} + volumeMounts: + {{- if .Values.debug.enabled }} + - mountPath: /code/ + name: reana-code + {{- end }} + - mountPath: {{ .Values.shared_storage.shared_volume_mount_path }} + name: reana-shared-volume + imagePullPolicy: IfNotPresent + restartPolicy: Never + volumes: + - name: reana-shared-volume + {{- if not (eq .Values.shared_storage.backend "hostpath") }} + persistentVolumeClaim: + claimName: {{ include "reana.prefix" . }}-shared-persistent-volume + readOnly: false + {{- else }} + hostPath: + path: {{ .Values.shared_storage.hostpath.root_path }} + {{- end }} + - name: reana-code + hostPath: + path: /code/reana-server +{{- end }} diff --git a/helm/reana/templates/reana-config.yaml b/helm/reana/templates/reana-config.yaml index 4fe8ca4e..4d913d4e 100644 --- a/helm/reana/templates/reana-config.yaml +++ b/helm/reana/templates/reana-config.yaml @@ -16,6 +16,11 @@ data: cern_ropo: {{ .Values.components.reana_ui.cern_ropo | default false }} hide_signup: {{ .Values.components.reana_ui.hide_signup | default false }} admin_email: {{ .Values.notifications.email_config.receiver | quote | default "null" }} + {{- if not (contains "forever" (.Values.interactive_sessions.maximum_inactivity_period | quote)) }} + maximum_interactive_session_inactivity_period: {{ .Values.interactive_sessions.maximum_inactivity_period | quote | default "null" }} + {{- else }} + maximum_interactive_session_inactivity_period: null + {{- end }} {{- if or (.Values.components.reana_ui.local_users) (eq (.Values.components.reana_ui.local_users | toString) "") }} local_users: true {{- else }} diff --git a/helm/reana/values.yaml b/helm/reana/values.yaml index e647c6ce..09bdff36 100644 --- a/helm/reana/values.yaml +++ b/helm/reana/values.yaml @@ -20,6 +20,10 @@ workspaces: paths: - /var/reana:/var/reana +interactive_sessions: + cronjob_schedule: "0 3 * * *" # everyday at 3am + maximum_inactivity_period: forever + compute_backends: - kubernetes diff --git a/reana/reana_dev/run.py b/reana/reana_dev/run.py index bda150e8..96bc7e8c 100644 --- a/reana/reana_dev/run.py +++ b/reana/reana_dev/run.py @@ -595,7 +595,6 @@ def _return_missing_output_files(component: str, workflow_name: str) -> List[str elif "failed" in status: run_statistics["failed"].append(workflow_name) elif "finished" in status or "stopped" in status: - if not _verify_log_output(component, workflow_name): run_statistics["failed"].append(workflow_name) continue diff --git a/tests/test_cli.py b/tests/test_cli.py index d8bcaf17..7fd8fb45 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -19,7 +19,7 @@ def test_shorten_component_name(): """Tests for shorten_component_name().""" from reana.reana_dev.utils import shorten_component_name - for (name_long, name_short) in ( + for name_long, name_short in ( ("", ""), ("reana", "reana"), ("reana-job-controller", "r-j-controller"), @@ -31,7 +31,7 @@ def test_get_expected_output_filenames_for_example(): """Tests for get_expected_output_filenames_for_example().""" from reana.reana_dev.run import get_expected_output_filenames_for_example - for (example, output) in ( + for example, output in ( ("", ("plot.png",)), ("reana-demo-helloworld", ("greetings.txt",)), ("reana-demo-root6-roofit", ("plot.png",)), @@ -44,7 +44,7 @@ def test_get_expected_log_message_for_example(): """Tests for get_expected_log_messages_for_example().""" from reana.reana_dev.run import get_expected_log_messages_for_example - for (example, output) in ( + for example, output in ( ("", ("job:",)), ("reana-demo-helloworld", ("Parameters: inputfile=",)), ): @@ -89,7 +89,7 @@ def test_select_components(): REPO_LIST_CLUSTER, ) - for (input_value, output_expected) in ( + for input_value, output_expected in ( # regular operation: (["reana-job-controller"], ["reana-job-controller"]), (["reana-job-controller", "reana"], ["reana-job-controller", "reana, "]), @@ -123,7 +123,7 @@ def test_select_workflow_engines(): """Tests for select_workflow_engines().""" from reana.reana_dev.run import select_workflow_engines - for (input_value, output_expected) in ( + for input_value, output_expected in ( # regular workflow engines: (["cwl"], ["cwl"]), (["serial"], ["serial"]), @@ -142,7 +142,7 @@ def test_find_standard_component_name(): """Tests for find_standard_component_name().""" from reana.reana_dev.utils import find_standard_component_name - for (input_value, output_expected) in ( + for input_value, output_expected in ( ("reana", "reana"), ("r-server", "reana-server"), ("r-j-controller", "reana-job-controller"), @@ -169,7 +169,7 @@ def test_construct_workflow_name(): """Tests for construct_workflow_name().""" from reana.reana_dev.run import construct_workflow_name - for (input_value, output_expected) in ( + for input_value, output_expected in ( (("reana", "cwl", "kubernetes"), "reana-cwl-kubernetes"), ( ("reana-demo-root6-roofit", "yadage", "htcondorcern"),