From 31c27eabb57425e833e4b21f5dda38393d6c318d Mon Sep 17 00:00:00 2001 From: Benjamin Isinger Date: Mon, 12 May 2025 12:27:40 +0200 Subject: [PATCH] wip: Add code-server sample --- code-server/.ceignore | 5 + code-server/Dockerfile | 35 +++ code-server/README.md | 121 +++++++++++ code-server/add-volume-mount-to-app.sh | 68 ++++++ code-server/build | 17 ++ code-server/config.yaml | 9 + code-server/run | 286 +++++++++++++++++++++++++ 7 files changed, 541 insertions(+) create mode 100644 code-server/.ceignore create mode 100644 code-server/Dockerfile create mode 100644 code-server/README.md create mode 100755 code-server/add-volume-mount-to-app.sh create mode 100755 code-server/build create mode 100644 code-server/config.yaml create mode 100755 code-server/run diff --git a/code-server/.ceignore b/code-server/.ceignore new file mode 100644 index 000000000..25e8d5c57 --- /dev/null +++ b/code-server/.ceignore @@ -0,0 +1,5 @@ +# Ignore everything +* + +# Allow Dockerfile only +!/Dockerfile diff --git a/code-server/Dockerfile b/code-server/Dockerfile new file mode 100644 index 000000000..3d28cae3c --- /dev/null +++ b/code-server/Dockerfile @@ -0,0 +1,35 @@ +# Start from the code-server Debian base image +FROM codercom/code-server + +USER root + +RUN chown -R coder:coder /home/coder + +# Install Java +RUN apt update && \ + apt install wget lsb-release -y && \ + wget https://packages.microsoft.com/config/debian/$(lsb_release -rs)/packages-microsoft-prod.deb -O packages-microsoft-prod.deb && \ + dpkg -i packages-microsoft-prod.deb + +RUN apt-get update && \ + apt-get install -y msopenjdk-21 + +# Install VS Code extensions +# Extensions will not be installed into the default directory (/extensions). +# Instead, they will be managed in a dedicated directory to ensure they remain uncoupled from the user-data directory. +# This prevents the extensions directory from changing if the user-data directory is overwritten or reconfigured at runtime. +USER coder + +ENV EXTENSION_DIR=/home/coder/extensions +RUN mkdir -p "${EXTENSION_DIR}" +RUN echo "[]" > "${EXTENSION_DIR}/extensions.json" + +# Install the required extensions using code-server with the specified extensions directory. +# Note: We use a different marketplace than VS Code. See https://github.com/cdr/code-server/blob/main/docs/FAQ.md#differences-compared-to-vs-code for more details. +RUN code-server --extensions-dir "${EXTENSION_DIR}" --install-extension redhat.java +RUN code-server --extensions-dir "${EXTENSION_DIR}" --install-extension vscjava.vscode-java-debug +RUN code-server --extensions-dir "${EXTENSION_DIR}" --install-extension vmware.vscode-spring-boot +RUN code-server --extensions-dir "${EXTENSION_DIR}" --install-extension vmware.vscode-boot-dev-pack + + +ENTRYPOINT /usr/bin/entrypoint.sh --extensions-dir ${EXTENSION_DIR} diff --git a/code-server/README.md b/code-server/README.md new file mode 100644 index 000000000..376bc879b --- /dev/null +++ b/code-server/README.md @@ -0,0 +1,121 @@ +# Code Server Sample for Java Development + + +## Setting up the example + +* Login to IBM Cloud via the CLI and target the desired region + ``` + REGION=eu-es + RESOURCE_GROUP=default + ibmcloud login -r ${REGION} -g ${RESOURCE_GROUP} + ``` +* Create the Code Engine project + ``` + CE_INSTANCE_NAME=code-server--ce + ibmcloud code-engine project create --name ${CE_INSTANCE_NAME} + + CE_INSTANCE_GUID=$(ibmcloud ce project current -o json | jq -r .guid) + ``` + +* Create the COS instance + ``` + COS_INSTANCE_NAME=code-server--cos + ibmcloud resource service-instance-create ${COS_INSTANCE_NAME} cloud-object-storage standard global + + COS_INSTANCE_ID=$(ibmcloud resource service-instance ${COS_INSTANCE_NAME} --output json | jq -r '.[0] | .id') + ``` +* Create the COS bucket + ``` + ibmcloud cos config crn --crn ${COS_INSTANCE_ID} + ibmcloud cos config auth --method IAM + ibmcloud cos config region --region ${REGION} + ibmcloud cos config endpoint-url --url s3.${REGION}.cloud-object-storage.appdomain.cloud + COS_BUCKET_NAME=${CE_INSTANCE_GUID}-code-server + ibmcloud cos bucket-create \ + --class smart \ + --bucket $COS_BUCKET_NAME + ``` +* Create the credentials to access the COS instance + ``` + COS_HMAC_CREDENTIALS=$(ibmcloud resource service-key-create code-server-cos-credentials Writer --instance-id $COS_INSTANCE_ID --parameters '{"HMAC":true}' --output JSON) + + COS_HMAC_CREDENTIALS_ACCESS_KEY_ID=$(echo "$COS_HMAC_CREDENTIALS"|jq -r '.credentials.cos_hmac_keys.access_key_id') + COS_HMAC_CREDENTIALS_SECRET_ACCESS_ID=$(echo "$COS_HMAC_CREDENTIALS"|jq -r '.credentials.cos_hmac_keys.secret_access_key') + ``` +* Obtain the KUBECONTEXT of the current Code Engine project + ``` + ibmcloud ce project select --id ${CE_INSTANCE_GUID} --kubecfg + ``` +* As a CE user create the following secret resource within the project + ``` + CE_SECRET_NAME=cos-secret-$COS_BUCKET_NAME + kubectl apply -f - </dev/null; then + print_error "'jq' tool is not installed" + exit 1 + fi + echo "Done!" +} + +# Clean up previous run +function clean() { + + # COS_INSTANCE_ID=$(ibmcloud resource service-instance ${COS_INSTANCE_NAME} --output json | jq -r '.[0] | .id') + + ibmcloud target -g $RESOURCE_GROUP_NAME + + ibmcloud resource service-key-delete ${COS_HMAC_CREDENTIALS_NAME} --force + + # delete cos instance + ibmcloud resource service-instance-delete ${COS_INSTANCE_NAME} -g $RESOURCE_GROUP_NAME --force + + # hard delete ce project + ibmcloud ce project delete --name $CE_PROJECT_NAME --hard --force + + # delete resource group + ibmcloud resource group-delete $RESOURCE_GROUP_NAME --force + + echo "Done!" +} + +# ============================== +# MAIN SCRIPT FLOW +# ============================== + +print_msg "\n======================================================" +print_msg " Setting up \"Code Server on Code Engine \" sample" +print_msg "======================================================\n" + +echo "" +echo "Please note: This script will install various IBM Cloud resources within the resource group '$RESOURCE_GROUP_NAME'." + +print_msg "\nChecking prerequisites ..." +check_prerequisites + +# Ensure that latest versions of used IBM Cloud ClI is installed +print_msg "\nPulling latest IBM Cloud CLI release ..." +#ibmcloud update --force +echo "Done!" + +# Ensure that latest versions of used IBM Cloud CLI plugins are installed +print_msg "\nInstalling required IBM Cloud CLI plugins ..." +#ibmcloud plugin install code-engine -f --quiet +#ibmcloud plugin install cos -f --quiet +echo "Done!" + + +if [[ "$1" == "clean" ]]; then + print_msg "\nCleaning up the remains of previous executions ..." + clean + print_success "\n==========================================\n DONE\n==========================================\n" + exit 0 +fi + +print_msg "\nTargetting IBM Cloud region '$REGION' ..." +ibmcloud target -r $REGION + +# +# Create the resource group, if it does not exist +if ! ibmcloud resource group $RESOURCE_GROUP_NAME --quiet >/dev/null 2>&1; then + print_msg "\nCreating resource group '$RESOURCE_GROUP_NAME' ..." + ibmcloud resource group-create $RESOURCE_GROUP_NAME +fi +print_msg "\nTargetting resource group '$RESOURCE_GROUP_NAME' ..." +ibmcloud target -g $RESOURCE_GROUP_NAME + +# +# Create the COS instance, if it does not exist +print_msg "\nInitializing the COS instance '$COS_INSTANCE_NAME' ..." +if ! ibmcloud resource service-instance ${COS_INSTANCE_NAME} 2>/dev/null; then + print_msg "\nCreating the COS instance '$COS_INSTANCE_NAME' ..." + ibmcloud resource service-instance-create ${COS_INSTANCE_NAME} cloud-object-storage standard global -d premium-global-deployment-iam +fi +COS_INSTANCE_ID=$(ibmcloud resource service-instance ${COS_INSTANCE_NAME} --output json | jq -r '.[0] | .id') +ibmcloud cos config crn --crn ${COS_INSTANCE_ID} --force +ibmcloud cos config auth --method IAM +ibmcloud cos config region --region ${REGION} +ibmcloud cos config endpoint-url --url s3.${REGION}.cloud-object-storage.appdomain.cloud + + +print_msg "\nListing buckets of the instance ..." +buckets=$(ibmcloud cos buckets -output json) +COS_BUCKET_NAME="" +if [[ "$(echo "${buckets}" | jq -r '.Buckets')" != "null" ]]; then + echo "Found $(echo "${buckets}" | jq -r '.Buckets|length') bucket(s):" + for bucket in $(echo "${buckets}" | jq -r '.Buckets|.[] | @base64'); do + _jq() { + echo ${bucket} | base64 --decode | jq -r ${1} + } + bucket_name=$(_jq '.Name') + echo "- $bucket_name" + + if [[ "$bucket_name" =~ ^ce-${REGION}-code-server-* ]]; then + COS_BUCKET_NAME=$bucket_name + fi + done +fi + +# +# Create a COS bucket, if it does not exist +if [[ $COS_BUCKET_NAME == "" ]]; then + random_chars=$(openssl rand -hex 6) + COS_BUCKET_NAME=ce-${REGION}-code-server-${random_chars} + print_msg "\nCreating the COS bucket '$COS_BUCKET_NAME' ..." + ibmcloud cos bucket-create \ + --class smart \ + --region $REGION \ + --ibm-service-instance-id $COS_INSTANCE_ID \ + --bucket $COS_BUCKET_NAME +else + print_msg "\nUsing the COS bucket '$COS_BUCKET_NAME' ..." + echo "Done!" +fi + +# +# Create the Code Engine project, if it does not exist +print_msg "\nInitializing the Code Engine project '$CE_PROJECT_NAME' ..." +if ! ibmcloud ce proj select --name $CE_PROJECT_NAME 2>/dev/null; then + print_msg "\nCreating Code Engine project '$CE_PROJECT_NAME' ..." + ibmcloud ce proj create --name $CE_PROJECT_NAME +fi +CE_PROJECT=$(ibmcloud ce project current --output json) +CE_PROJECT_GUID=$(echo "$CE_PROJECT" | jq -r '.guid') +CE_PROJECT_DOMAIN=$(echo "$CE_PROJECT" | jq -r '.domain') +CE_PROJECT_NAMESPACE=$(echo "$CE_PROJECT" | jq -r '.kube_config_context') + +# +# Obtain the Kubernetes context of the Code Engine project +print_msg "\nObtaining the Kube config of the Code Engine project ..." +ibmcloud ce project select --id ${CE_PROJECT_GUID} --kubecfg + +print_msg "\nInitializing HMAC credentials to access the COS instance '$COS_INSTANCE_ID' ..." +CE_SECRET_NAME=cos-secret-$COS_BUCKET_NAME +if ibmcloud ce secret get --name $CE_SECRET_NAME --quiet >/dev/null 2>&1; then + echo "Done! Code Engine secret '$CE_SECRET_NAME' does exist" +else + + # + # Create the credentials to access the COS instance + print_msg "\nCreating the HMAC credentials '$COS_HMAC_CREDENTIALS_NAME' to access the COS instance ..." + COS_HMAC_CREDENTIALS=$(ibmcloud resource service-key-create ${COS_HMAC_CREDENTIALS_NAME} Writer --instance-id $COS_INSTANCE_ID --parameters '{"HMAC":true}' --output JSON) + COS_HMAC_CREDENTIALS_ACCESS_KEY_ID=$(echo "$COS_HMAC_CREDENTIALS"|jq -r '.credentials.cos_hmac_keys.access_key_id') + COS_HMAC_CREDENTIALS_SECRET_ACCESS_ID=$(echo "$COS_HMAC_CREDENTIALS"|jq -r '.credentials.cos_hmac_keys.secret_access_key') + + # + # Store the HMAC credentials in a Code Engine secret + + print_msg "\nStore the HMAC credentials in a Code Engine secret '$CE_SECRET_NAME' ..." + kubectl apply -f - </dev/null 2>&1; then + echo "Done! Code Engine persistent storage '$CE_STORAGE_NAME' does exist" +else + print_msg "\nCreating Code Engine persistent storage '$CE_STORAGE_NAME' ..." + kubectl apply -f - </dev/null 2>&1; then + print_msg "\nCreating the configuration '$CE_CONFIG_NAME' ..." + ibmcloud ce configmap create --name $CE_CONFIG_NAME --from-file config.yaml +else + echo "Done!" +fi + +# Deploy the Code Engine app to run the code server +print_msg "\nInitializing the code-server app '$CE_APP_CODE_SERVER' ..." +if ! ibmcloud ce app get --name $CE_APP_CODE_SERVER >/dev/null 2>&1; then + print_msg "\nCreating the code server app '$CE_APP_CODE_SERVER' ..." + ibmcloud ce app create --name $CE_APP_CODE_SERVER \ + --build-source "." \ + --min-scale 1 \ + --port 12345 \ + --cpu 8 --memory 16G \ + --command "/bin/bash" --command "-c" \ + --argument '/usr/bin/entrypoint.sh --extensions-dir ${EXTENSION_DIR} /data/workspace' \ + --env CODE_SERVER_CONFIG=/home/coder/.config/code-server/config.yaml \ + --env MAVEN_OPTS="-Dmaven.repo.local=/data/cache/.m2" \ + --env VSCODE_PROXY_URI=./absproxy/{{port}} \ + --argument /data/workspace \ + --scale-down-delay 600 \ + --no-wait + + print_msg "\nConfiguring the volume mounts for the app '$CE_APP_CODE_SERVER' ..." + ./add-volume-mount-to-app.sh $CE_APP_CODE_SERVER $CE_STORAGE_NAME /data + sleep 3 + +else + echo "Done!" +fi + +print_msg "\nUpdate the app '$CE_APP_CODE_SERVER' ..." +ibmcloud ce app update --name $CE_APP_CODE_SERVER \ + --build-source "." + +ROOT_DOMAIN=.${CE_PROJECT_NAMESPACE}.${CE_PROJECT_DOMAIN} +FQDN_CODE_SERVER_APP=${CE_APP_CODE_SERVER}${ROOT_DOMAIN} +URL_CODE_SERVER_APP=https://${FQDN_CODE_SERVER_APP} + +print_msg "\nThis end-to-end sample created the following set of IBM Cloud resources:" +ibmcloud resource service-instances --type all -g $RESOURCE_GROUP_NAME + +print_msg "\nSo far, following files have been stored in the COS bucket '$COS_BUCKET_NAME':" +ibmcloud cos objects --bucket $COS_BUCKET_NAME + +echo "" +ibmcloud ce app list + +print_msg "\nFollowing commands can be used to further play around with the sample setup:" +echo "1. Open the browser and type '$URL_CODE_SERVER_APP' to access code server" +echo "2. Tear down the sample setup: './run clean'" + +print_success "\n==========================================" +print_success " SUCCESS" +print_success "==========================================\n"