diff --git a/.icons/rstudio.svg b/.icons/rstudio.svg new file mode 100644 index 000000000..f174e1c22 --- /dev/null +++ b/.icons/rstudio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/registry/coder/.images/rstudio-server.png b/registry/coder/.images/rstudio-server.png new file mode 100644 index 000000000..71ecc4648 Binary files /dev/null and b/registry/coder/.images/rstudio-server.png differ diff --git a/registry/coder/modules/rstudio-server/README.md b/registry/coder/modules/rstudio-server/README.md new file mode 100644 index 000000000..3bae54340 --- /dev/null +++ b/registry/coder/modules/rstudio-server/README.md @@ -0,0 +1,25 @@ +--- +display_name: RStudio Server +description: Deploy the Rocker Project distribution of RStudio Server in your Coder workspace. +icon: ../../../../.icons/rstudio.svg +verified: true +tags: [rstudio, ide, web] +--- + +# RStudio Server + +> [!NOTE] +> This module requires `docker` to be available in the workspace. Check [Docker in Workspaces](https://coder.com/docs/admin/templates/extending-templates/docker-in-workspaces) to learn how you can set it up. + +Deploy the Rocker Project distribution of RStudio Server in your Coder workspace. + +![RStudio Server](../../.images/rstudio-server.png) + +```tf +module "rstudio-server" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/rstudio-server/coder" + version = "0.9.0" + agent_id = coder_agent.example.id +} +``` diff --git a/registry/coder/modules/rstudio-server/main.tf b/registry/coder/modules/rstudio-server/main.tf new file mode 100644 index 000000000..3b94b08da --- /dev/null +++ b/registry/coder/modules/rstudio-server/main.tf @@ -0,0 +1,123 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + coder = { + source = "coder/coder" + version = ">= 2.5" + } + } +} + +# Add required variables for your modules and remove any unneeded variables +variable "agent_id" { + type = string + description = "The ID of a Coder agent." +} + +variable "docker_socket" { + type = string + description = "(Optional) Docker socket URI" + default = "" +} + +variable "rstudio_server_version" { + type = string + description = "RStudio Server version" + default = "4.5.1" +} + +variable "disable_auth" { + type = bool + description = "Disable auth" + default = true +} + +variable "rstudio_user" { + type = string + description = "RStudio user" + default = "rstudio" + sensitive = true +} + +variable "rstudio_password" { + type = string + description = "RStudio password" + default = "rstudio" + sensitive = true +} + +variable "project_path" { + type = string + description = "The path to RStudio project, it will be mounted in the container." + default = null +} + +variable "port" { + type = number + description = "The port to run rstudio-server on." + default = 8787 +} + +variable "enable_renv" { + type = bool + description = "If renv.lock exists, renv will restore the environment and install dependencies" + default = true +} + +variable "renv_cache_volume" { + type = string + description = "The name of the volume used by Renv to preserve dependencies between container restarts" + default = "renv-cache-volume" +} + +variable "share" { + type = string + default = "owner" + validation { + condition = var.share == "owner" || var.share == "authenticated" || var.share == "public" + error_message = "Incorrect value. Please set either 'owner', 'authenticated', or 'public'." + } +} + +variable "order" { + type = number + description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)." + default = null +} + +variable "group" { + type = string + description = "The name of a group that this app belongs to." + default = null +} + +resource "coder_script" "rstudio-server" { + agent_id = var.agent_id + display_name = "rstudio-server" + icon = "/icon/rstudio.svg" + script = templatefile("${path.module}/run.sh", { + DOCKER_HOST : var.docker_socket, + SERVER_VERSION : var.rstudio_server_version, + DISABLE_AUTH : var.disable_auth, + RSTUDIO_USER : var.rstudio_user, + RSTUDIO_PASSWORD : var.rstudio_password, + PROJECT_PATH : var.project_path, + PORT : var.port, + ENABLE_RENV : var.enable_renv, + RENV_CACHE_VOLUME : var.renv_cache_volume, + }) + run_on_start = true +} + +resource "coder_app" "rstudio-server" { + agent_id = var.agent_id + slug = "rstudio-server" + display_name = "RStudio Server" + url = "http://localhost:${var.port}" + icon = "/icon/rstudio.svg" + subdomain = true + share = var.share + order = var.order + group = var.group +} diff --git a/registry/coder/modules/rstudio-server/run.sh b/registry/coder/modules/rstudio-server/run.sh new file mode 100644 index 000000000..a9acbfb28 --- /dev/null +++ b/registry/coder/modules/rstudio-server/run.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env sh + +set -eu + +BOLD='\033[0;1m' +RESET='\033[0m' + +printf "$${BOLD}Starting RStudio Server (Rocker)...$${RESET}\n" + +# Wait for docker to become ready +max_attempts=10 +delay=2 +attempt=1 + +while ! docker ps; do + if [ $attempt -ge $max_attempts ]; then + echo "Failed to list containers after $${max_attempts} attempts." + exit 1 + fi + echo "Attempt $${attempt} failed, retrying in $${delay}s..." + sleep $delay + attempt=$(expr "$attempt" + 1) + delay=$(expr "$delay" \* 2) # exponential backoff +done + +# Pull the specified version +IMAGE="rocker/rstudio:${SERVER_VERSION}" +docker pull "$${IMAGE}" + +# Create (or reuse) a persistent renv cache volume +docker volume create "${RENV_CACHE_VOLUME}" + +# Run container (auto-remove on stop) +docker run -d --rm \ + --name rstudio-server \ + -p "${PORT}:8787" \ + -e DISABLE_AUTH="${DISABLE_AUTH}" \ + -e USER="${RSTUDIO_USER}" \ + -e PASSWORD="${RSTUDIO_PASSWORD}" \ + -e RENV_PATHS_CACHE="/renv/cache" \ + -v "${PROJECT_PATH}:/home/${RSTUDIO_USER}/project" \ + -v "${RENV_CACHE_VOLUME}:/renv/cache" \ + "$${IMAGE}" + +# Make RENV_CACHE_VOLUME writable to USER +docker exec rstudio-server bash -c 'chmod -R 0777 /renv/cache' + +# Optional renv restore +if [ "${ENABLE_RENV}" = "true" ] && [ -f "${PROJECT_PATH}/renv.lock" ]; then + echo "Restoring R environment via renv..." + docker exec -u "${RSTUDIO_USER}" rstudio-server R -q -e \ + 'if (!requireNamespace("renv", quietly = TRUE)) install.packages("renv", repos="https://cloud.r-project.org"); renv::restore(prompt = FALSE)' +fi + +[ "${DISABLE_AUTH}" != "true" ] && echo "User: ${RSTUDIO_USER}" + +printf "\n$${BOLD}RStudio Server ${SERVER_VERSION} is running on port ${PORT}$${RESET}\n"