diff --git a/docs/roles/jupyterhub_app.md b/docs/roles/jupyterhub_app.md index 11db5388..9f6daac0 100644 --- a/docs/roles/jupyterhub_app.md +++ b/docs/roles/jupyterhub_app.md @@ -106,8 +106,8 @@ The resulting config for a JupyterLab server extension will look as follows: settings = { "Example": {'command': ['/usr/bin/python3', '-m' 'http.server', '-p', '{port}'], 'launcher_entry': {'icon_path': '/path/to/my/icon.svg', 'category': 'Other'} } -c.ServerProxy.servers.update({ -}) # use update instead of assignment so multiple apps can be added to the config. +c.ServerProxy.servers.update(settings +) # use update instead of assignment so multiple apps can be added to the config. ``` The resulting config for a standalone app will look as follows: diff --git a/docs/roles/rstudio.md b/docs/roles/rstudio.md index b9e3326f..545eb992 100644 --- a/docs/roles/rstudio.md +++ b/docs/roles/rstudio.md @@ -2,16 +2,22 @@ [back to index](../index.md#Roles) ## Summary -Installs R-studio on Ubuntu 20.04 and 22.04 +Installs RStudio Desktop or Server. ## Requires -Ubuntu 20.04 or 22.04 and an installation of R (e.g. by executing the r-workbench playbook first) +- Ubuntu >= 20.04 +- Untested supported for RHEL-like Linux ## Description Installation is done using apt and the debian package available [here](https://posit.co/download/rstudio-desktop/). +## Variables + +- `rstudio_kind`: String. Whether to install the desktop or server version of RStudio. Default: `desktop` (set to `server` alternatively). + ## History 2022 Written by Sytse Groenwold (Universiteit Utrecht) 2024 Updated by Jelle Treep (Utrecht University) +2025 Updated by Dawa Ometto (Utrecht University) [back to index](../index.md#Roles) diff --git a/molecule/playbook-rstudio_server/molecule.yml b/molecule/playbook-rstudio_server/molecule.yml new file mode 100644 index 00000000..74084bb4 --- /dev/null +++ b/molecule/playbook-rstudio_server/molecule.yml @@ -0,0 +1,29 @@ +--- +platforms: + - name: workspace-src-ubuntu_jammy-nginx + image: ghcr.io/utrechtuniversity/src-test-workspace:ubuntu_jammy-nginx + command: /sbin/init + pre_build_image: true + published_ports: + - 8080:80 + registry: + url: $DOCKER_REGISTRY + credentials: + username: $DOCKER_USER + password: $DOCKER_PW +provisioner: + name: ansible + env: + components: + - name: jupyterhub + path: jupyterhub.yml + parameters: + jupyter_auth: noauth + jupyter_activate_remote_user_auth: true # mock SRAM auth by enabling remote user auth in JupyterHub config, despite setting auth to noauth. + jupyter_proxy_config: "{ proxy_set_header: { REMOTE_USER: '$$http_user'} }" + - name: rstudio + path: rstudio_server.yml + parameters: + rstudio_base_path: "/rstudio" + rstudio_standalone: 'false' + jupyterhub_app_pip_executable: uv_pip diff --git a/molecule/playbook-rstudio_server/verify.yml b/molecule/playbook-rstudio_server/verify.yml new file mode 100644 index 00000000..e8d7bda4 --- /dev/null +++ b/molecule/playbook-rstudio_server/verify.yml @@ -0,0 +1,22 @@ +--- +- name: Verify + hosts: all + gather_facts: false + tasks: + - name: Test rstudio + ansible.builtin.uri: + url: http://localhost/hub/spawn/testuser?next=%2Fhub%2Fuser%2Ftestuser%2Frstudio%2F + method: GET + headers: + user: testuser # mock SRAM login + return_content: true + follow_redirects: true + register: rstudio_login + retries: 6 + delay: 5 + + + - name: Assert content correct + ansible.builtin.assert: + that: + - '"Posit" in rstudio_login.content' diff --git a/playbooks/roles/jupyterhub_app/meta/main.yml b/playbooks/roles/jupyterhub_app/meta/main.yml index 4bd3e02c..7e0b004f 100644 --- a/playbooks/roles/jupyterhub_app/meta/main.yml +++ b/playbooks/roles/jupyterhub_app/meta/main.yml @@ -11,3 +11,5 @@ galaxy_info: - name: EL versions: - all # not tested for specific versions +dependencies: + - role: system_python diff --git a/playbooks/roles/rstudio/defaults/main.yml b/playbooks/roles/rstudio/defaults/main.yml index bd44a4b6..7e385548 100644 --- a/playbooks/roles/rstudio/defaults/main.yml +++ b/playbooks/roles/rstudio/defaults/main.yml @@ -1,2 +1,3 @@ --- r_version: 4.0.5 +rstudio_kind: desktop diff --git a/playbooks/roles/rstudio/molecule/default/molecule.yml b/playbooks/roles/rstudio/molecule/default/molecule.yml index 617b4deb..be76b672 100644 --- a/playbooks/roles/rstudio/molecule/default/molecule.yml +++ b/playbooks/roles/rstudio/molecule/default/molecule.yml @@ -9,7 +9,7 @@ provisioner: role_name_check: 1 platforms: - name: workspace-src-ubuntu_desktop - image: ghcr.io/utrechtuniversity/src-test-workspace:ubuntu_focal-desktop + image: ghcr.io/utrechtuniversity/src-test-workspace:ubuntu_jammy-desktop pre_build_image: true registry: url: $DOCKER_REGISTRY diff --git a/playbooks/roles/rstudio/molecule/server/converge.yml b/playbooks/roles/rstudio/molecule/server/converge.yml new file mode 100644 index 00000000..278648e9 --- /dev/null +++ b/playbooks/roles/rstudio/molecule/server/converge.yml @@ -0,0 +1,10 @@ +--- +- name: Converge + hosts: all + gather_facts: true + tasks: + - name: Testing rstudio role + ansible.builtin.include_role: + name: rstudio + vars: + rstudio_kind: server diff --git a/playbooks/roles/rstudio/molecule/server/molecule.yml b/playbooks/roles/rstudio/molecule/server/molecule.yml new file mode 100644 index 00000000..8ed76b66 --- /dev/null +++ b/playbooks/roles/rstudio/molecule/server/molecule.yml @@ -0,0 +1,18 @@ +--- +provisioner: + name: ansible + playbooks: + converge: ./converge.yml + prepare: ./prepare.yml + env: + ANSIBLE_ROLES_PATH: ../../../ +role_name_check: 1 +platforms: + - name: workspace-src-ubuntu_desktop + image: ghcr.io/utrechtuniversity/src-test-workspace:ubuntu_jammy + pre_build_image: true + registry: + url: $DOCKER_REGISTRY + credentials: + username: $DOCKER_USER + password: $DOCKER_PW diff --git a/playbooks/roles/rstudio/tasks/debian.yml b/playbooks/roles/rstudio/tasks/debian.yml index 2f5b0b15..adbd99d0 100644 --- a/playbooks/roles/rstudio/tasks/debian.yml +++ b/playbooks/roles/rstudio/tasks/debian.yml @@ -10,14 +10,7 @@ name: r-base-dev state: present -- name: Install RStudio for Ubuntu 20 - when: ansible_distribution == "Ubuntu" and ansible_distribution_version == "20.04" +- name: Install RStudio ansible.builtin.apt: - deb: https://download1.rstudio.org/electron/focal/amd64/rstudio-2024.04.2-764-amd64.deb - state: present - -- name: Install RStudio for Ubuntu 22 - when: ansible_distribution == "Ubuntu" and ansible_distribution_version == "22.04" - ansible.builtin.apt: - deb: https://download1.rstudio.org/electron/jammy/amd64/rstudio-2025.05.0-496-amd64.deb + deb: "{{ rstudio_links['Ubuntu'][ansible_distribution_release][rstudio_kind] }}" state: present diff --git a/playbooks/roles/rstudio/tasks/redhat.yml b/playbooks/roles/rstudio/tasks/redhat.yml index f8a6edfa..8bc01d40 100644 --- a/playbooks/roles/rstudio/tasks/redhat.yml +++ b/playbooks/roles/rstudio/tasks/redhat.yml @@ -4,9 +4,9 @@ name: epel-release state: present -- name: Install R +- name: Install R and RStudio ansible.builtin.package: name: - R - - https://s3.amazonaws.com/rstudio-ide-build/desktop/rhel8/x86_64/rstudio-2022.02.0-443-x86_64.rpm + - "{{ rstudio_links['RedHat'][rstudio_kind] }}" state: present diff --git a/playbooks/roles/rstudio/vars/main.yml b/playbooks/roles/rstudio/vars/main.yml new file mode 100644 index 00000000..e6642695 --- /dev/null +++ b/playbooks/roles/rstudio/vars/main.yml @@ -0,0 +1,13 @@ +rstudio_links: + Ubuntu: + focal: + desktop: https://download1.rstudio.org/electron/focal/amd64/rstudio-2024.04.2-764-amd64.deb + jammy: + desktop: https://download1.rstudio.org/electron/jammy/amd64/rstudio-2025.09.2-418-amd64.deb + server: https://download2.rstudio.org/server/jammy/amd64/rstudio-server-2025.09.2-418-amd64.deb + noble: + desktop: https://download1.rstudio.org/electron/jammy/amd64/rstudio-2025.09.2-418-amd64.deb + server: https://download2.rstudio.org/server/jammy/amd64/rstudio-server-2025.09.2-418-amd64.deb + RedHat: + desktop: https://download1.rstudio.org/electron/rhel9/x86_64/rstudio-2025.09.2-418-x86_64.rpm + server: https://download2.rstudio.org/server/rhel9/x86_64/rstudio-server-rhel-2025.09.2-418-x86_64.rpm diff --git a/playbooks/rstudio_server.yml b/playbooks/rstudio_server.yml new file mode 100644 index 00000000..50a642c4 --- /dev/null +++ b/playbooks/rstudio_server.yml @@ -0,0 +1,68 @@ +--- +- name: Install RStudio Server in JupyterHub + hosts: localhost + gather_facts: true + vars: + _rstudio_fqdn: "{{ ('molecule-notest' in ansible_skip_tags) | ternary('localhost', rstudio_fqdn) }}" + _rstudio_base_path: "{{ rstudio_base_path | default('/', true) }}" + _rstudio_jupyter_venv_path: "{{ rstudio_jupyter_venv_path | default('/usr/local/jupyterhub', true) }}" + _rstudio_standalone: "{{ rstudio_standalone | default(false, true) | bool }}" # should be set to true unless a previous component has already installed JupyterHub. + pre_tasks: + - name: Get installed R kernels + when: not _rstudio_standalone + block: + - name: Get all kernels + ansible.builtin.command: "{{ _rstudio_jupyter_venv_path }}/bin/jupyter kernelspec list --json" + register: all_kernels_json + changed_when: false + + - name: Get R kernels + ansible.builtin.set_fact: + all_r_kernels: "{{ (all_kernels_json.stdout | from_json)['kernelspecs'].values() | map(attribute='spec') | selectattr('language', 'equalto', 'R') | list }}" + roles: + - role: rstudio + vars: + rstudio_kind: server + - role: jupyterhub_standalone_proxy + when: _rstudio_standalone + # install jupyterhub and rstudio as a standalone app. + vars: + jupyterhub_uri: "{{ _rstudio_base_path }}" + jhsp_external_addr: "{{ _rstudio_fqdn }}" + - role: jupyterhub_app + vars: + jupyterhub_app_name: rstudio + jupyterhub_app_venv: "{{ _rstudio_jupyter_venv_path | default('') }}" + jupyterhub_app_config_dir: "{{ rstudio_jupyter_config_dir | default('') }}" + jupyterhub_app_pip_executable: "{{ rstudio_pip_cmd | default('uv_pip', true) }}" + jupyterhub_app_server_config: | + from jupyter_rsession_proxy import setup_rserver + c.ServerProxy.servers.update({ + {% for server in all_r_kernels | default([]) %} + "rstudio{{ loop.index }}": setup_rserver(prefix="rstudio{{ loop.index }}", r_path="{{ server['argv'][0] }}", launcher_title="RStudio ({{ server['display_name']}})"), + {% endfor %} + }) + jupyterhub_app_standalone_config: | + from utils import * + from jupyter_rsession_proxy import setup_rserver + patch_check_origin() # apply monkeypatch for custom CORS allowlist for websockets + + config = setup_rserver(prefix="") + c.StandaloneProxyServer.unix_socket = True + c.StandaloneProxyServer.command = config['command'] + c.StandaloneProxyServer.rewrite_response = config['rewrite_response'] + c.StandaloneProxyServer.timeout = config['timeout'] + c.StandaloneProxyServer.environment = config['environment'] + + tasks: + - name: Install jupyter-rsession-proxy + notify: Restart JupyterHub + ansible.builtin.pip: + name: git+https://github.com/dometto/jupyter-rsession-proxy.git@multiple_servers + state: present + virtualenv: "{{ _rstudio_jupyter_venv_path }}" + handlers: + - name: Restart JupyterHub + ansible.builtin.service: + state: restarted + name: jupyterhub