Skip to content

Commit

Permalink
Merge branch 'dev' into feature/workbench-session
Browse files Browse the repository at this point in the history
  • Loading branch information
zachhannum authored Nov 21, 2024
2 parents d6fc4d2 + 6363aef commit 2944d3b
Show file tree
Hide file tree
Showing 10 changed files with 393 additions and 61 deletions.
57 changes: 22 additions & 35 deletions .github/workflows/build-bake.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,39 +37,6 @@ jobs:
GIT_SHA=$(git rev-parse --short HEAD)
echo "GIT_SHA=$GIT_SHA" >> $GITHUB_OUTPUT
echo "$GIT_SHA"
versions:
name: Fetch Workbench session init container version
runs-on: ubuntu-latest

concurrency:
group: fetch-versions-${{ github.ref }}
cancel-in-progress: true

outputs:
WORKBENCH_SESSION_INIT_VERSION: ${{ steps.get-version.outputs.WORKBENCH_SESSION_INIT_VERSION }}

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Just
uses: extractions/setup-just@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install Python dependencies
run: |
pip install requests
- name: Get Version
id: get-version
run: |
WORKBENCH_SESSION_INIT_VERSION=$(just -f ci.Justfile get-version workbench --type=daily --local)
echo "WORKBENCH_SESSION_INIT_VERSION=$WORKBENCH_SESSION_INIT_VERSION" >> $GITHUB_OUTPUT
base:
needs: [setup]
Expand Down Expand Up @@ -366,7 +333,7 @@ jobs:
snyk-token: '${{ secrets.SNYK_TOKEN }}'

workbench-session-init:
needs: [setup, versions]
needs: [setup]
name: Workbench Session Init
runs-on: ubuntu-latest-8x

Expand All @@ -377,7 +344,6 @@ jobs:
env:
target: workbench-session-init
GIT_SHA: ${{ needs.setup.outputs.GIT_SHA }}
WORKBENCH_SESSION_INIT_VERSION: ${{ needs.versions.outputs.WORKBENCH_SESSION_INIT_VERSION }}

steps:
- name: Checkout
Expand All @@ -396,7 +362,28 @@ jobs:
with:
buildkitd-config: ./share/buildkitd.toml

- name: Set up Just
uses: extractions/setup-just@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install Python dependencies
run: |
pip install requests
- name: Get Version
id: get-version
run: |
WORKBENCH_SESSION_INIT_VERSION=$(just -f ci.Justfile get-version workbench --type=daily --local)
echo "WORKBENCH_SESSION_INIT_VERSION=$WORKBENCH_SESSION_INIT_VERSION" >> $GITHUB_OUTPUT
- name: Build, Test, and Push
env:
WORKBENCH_SESSION_INIT_VERSION: ${{ steps.get-version.outputs.WORKBENCH_SESSION_INIT_VERSION }}
uses: ./.github/actions/bake-test-push
with:
target: ${{ env.target }}
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/build-manual.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ jobs:
product="CONNECT"
elif [[ "$product" == "package-manager" ]]; then
product="PACKAGE_MANAGER"
elif [[ "$product" == "workbench-session-init" ]]; then
product="WORKBENCH_SESSION_INIT"
else
product="WORKBENCH"
fi
Expand Down
3 changes: 1 addition & 2 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ bake target="default":
docker buildx bake --builder=posit-builder -f docker-bake.hcl {{target}}

# just preview-bake workbench-images dev
preview-build:
just preview-bake "default"
alias preview-build := preview-bake
preview-bake target branch="$(git branch --show-current)":
just -f {{justfile()}} create-builder || true
WORKBENCH_DAILY_VERSION=$(just -f ci.Justfile get-version workbench --type=daily --local) \
Expand Down
39 changes: 33 additions & 6 deletions workbench-session-init/Dockerfile.ubuntu2204
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM ubuntu:22.04 AS build
FROM ubuntu:22.04 AS builder

# Install required tools:
# - ca-certificates installs necessary certificates to use cURL with HTTPS websites
Expand All @@ -9,17 +9,44 @@ RUN apt-get update && \
rm -rf /var/lib/apt/lists/*

ARG RSW_VERSION=2024.09.1+394.pro7
ARG GO_VERSION=1.22.2

# Download the RStudio Workbench session components and install Go
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN mkdir -p /pwb-staging && \
RSW_VERSION_URL=$(echo -n "${RSW_VERSION}" | sed 's/+/-/g') && \
echo "Downloading https://s3.amazonaws.com/rstudio-ide-build/session/multi/x86_64/rsp-session-multi-linux-${RSW_VERSION_URL}-x86_64.tar.gz" && \
curl -fsSL -o /pwb-staging/rsp-session-multi-linux.tar.gz "https://s3.amazonaws.com/rstudio-ide-build/session/multi/x86_64/rsp-session-multi-linux-${RSW_VERSION_URL}-x86_64.tar.gz" && \
mkdir -p /opt/session-components && \
tar -C /opt/session-components -xf /pwb-staging/rsp-session-multi-linux.tar.gz && \
chmod -R 755 /opt/session-components && \
tar -C /opt/session-components -xpf /pwb-staging/rsp-session-multi-linux.tar.gz && \
chmod 755 /opt/session-components && \
curl -fsSL -o /pwb-staging/go.tar.gz "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" && \
tar -C /usr/local -xf /pwb-staging/go.tar.gz && \
rm -rf /pwb-staging

COPY --chmod=755 run.sh /usr/local/bin/run.sh
# Add Go binary to PATH
ENV PATH="/usr/local/go/bin:$PATH"

# Set the Go workspace
WORKDIR /workspace

# Copy the Go source code and download dependencies
COPY entrypoint/go.mod entrypoint/go.sum ./
RUN go mod download

# Copy the Go source code and build the binary
COPY entrypoint/main.go .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags '-s -w' -o entrypoint main.go

# Create the final image
FROM ubuntu:22.04 AS build

ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates curl && \
rm -rf /var/lib/apt/lists/*

# Copy the compiled Go binary and session components from the builder stage
COPY --from=builder --chmod=755 /workspace/entrypoint /usr/local/bin/entrypoint
COPY --from=builder --chmod=755 /opt/session-components /opt/session-components

ENTRYPOINT ["/usr/local/bin/run.sh"]
ENTRYPOINT ["/usr/local/bin/entrypoint"]
9 changes: 9 additions & 0 deletions workbench-session-init/entrypoint/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module entrypoint

go 1.22.2

require (
github.com/otiai10/copy v1.14.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
)
6 changes: 6 additions & 0 deletions workbench-session-init/entrypoint/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
170 changes: 170 additions & 0 deletions workbench-session-init/entrypoint/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package main

import (
"fmt"
"io"
"os"
"path/filepath"
"time"

cp "github.com/otiai10/copy"
)

const (
sourceDir = "/opt/session-components"
targetDir = "/mnt/init"
)

var (
// Read the PWB_SESSION_TYPE environment variable
sessionType = os.Getenv("PWB_SESSION_TYPE")

// Set the copy options.
// Preserve permissions, times, and owner.
opt = cp.Options{
PermissionControl: cp.PerservePermission,
PreserveTimes: true,
PreserveOwner: true,
NumOfWorkers: 20,
}

// List of dependencies common to all session types
commonDeps = []string{
"bin/git-credential-pwb",
"bin/focal",
"bin/jammy",
"bin/noble",
"bin/opensuse15",
"bin/postback",
"bin/pwb-supervisor",
"bin/quarto",
"bin/r-ldpath",
"bin/rhel8",
"bin/rhel9",
"bin/shared-run",
"R",
"resources",
"www",
"www-symbolmaps",
}

// Map of session-specific dependencies
sessionDeps = map[string][]string{
"jupyter": {
"bin/jupyter-session-run",
"bin/node",
"extras",
},
"positron": {
"bin/positron-server",
"bin/positron-session-run",
"extras",
},
"rstudio": {
"bin/node",
"bin/rsession-run",
},
"vscode": {
"bin/pwb-code-server",
"bin/vscode-session-run",
"extras",
},
}
)

func main() {
if sessionType == "" {
fmt.Println("PWB_SESSION_TYPE environment variable is not set")
os.Exit(1)
}

programStart := time.Now()
defer func() {
elapsed := time.Since(programStart)
fmt.Printf("Program took %s\n", elapsed)
}()

filesToCopy, err := getFilesToCopy(sessionType)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

err = validateTargetDir(targetDir)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

err = copyFiles(sourceDir, targetDir, filesToCopy)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("Copy operation completed.")
}

// getFilesToCopy returns the list of files to copy based on the session type.
func getFilesToCopy(sessionType string) ([]string, error) {
files := commonDeps
if deps, ok := sessionDeps[sessionType]; ok {
files = append(files, deps...)
} else {
return nil, fmt.Errorf("unknown session type: %s", sessionType)
}
return files, nil
}

// validateTargetDir checks if the target directory exists and is empty.
func validateTargetDir(targetDir string) error {
if _, err := os.Stat(targetDir); os.IsNotExist(err) {
return fmt.Errorf("cannot find the copy target %s", targetDir)
}

isEmpty, err := isDirEmpty(targetDir)
if err != nil {
return fmt.Errorf("error checking if target directory is empty: %v", err)
}
if !isEmpty {
return fmt.Errorf("target directory %s is not empty", targetDir)
}

return nil
}

// isDirEmpty checks if a directory is empty.
func isDirEmpty(dir string) (bool, error) {
f, err := os.Open(dir)
if err != nil {
return false, err
}
defer f.Close()

_, err = f.ReadDir(1)
if err == io.EOF {
return true, nil
}
return false, err
}

// copyFiles copies the files from the source directory to the target directory.
// It uses the otiai10/copy package to copy files, with options to preserve
// permissions, times, and owner.
func copyFiles(src, dst string, filesToCopy []string) error {
fmt.Printf("Copying files from %s to %s\n", src, dst)
start := time.Now()

for _, file := range filesToCopy {
srcPath := filepath.Join(src, file)
dstPath := filepath.Join(dst, file)
err := cp.Copy(srcPath, dstPath, opt)
if err != nil {
return fmt.Errorf("error copying %s: %v", srcPath, err)
}
}

elapsed := time.Since(start)
fmt.Printf("Copy operation took %s\n", elapsed)

return nil
}
Loading

0 comments on commit 2944d3b

Please sign in to comment.