-
Notifications
You must be signed in to change notification settings - Fork 932
feat(examples): add Go and Java sandbox templates #782
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| # syntax=docker/dockerfile:1.7 | ||
| # | ||
| # Demo: a Cube-ready sandbox image that runs a tiny Go HTTP server on :8080 | ||
| # while the cubesandbox-base entrypoint keeps envd running on :49983 in the | ||
| # background. A good starting point for Go workloads in CubeSandbox. | ||
| # | ||
| # Build: | ||
| # docker build -t cubesandbox-demo-go:latest examples/cubesandbox-base-go | ||
| # | ||
| # Run locally: | ||
| # docker run --rm -d -p 8080:8080 -p 49983:49983 \ | ||
| # --name cube-demo-go cubesandbox-demo-go:latest | ||
| # curl http://127.0.0.1:8080/ | ||
| # curl -o /dev/null -w "%{http_code}\n" http://127.0.0.1:49983/health # => 204 | ||
|
|
||
| ARG CUBE_BASE_IMAGE=ghcr.io/tencentcloud/cubesandbox-base:2026.16 | ||
| FROM ${CUBE_BASE_IMAGE} | ||
|
|
||
| ARG DEBIAN_FRONTEND=noninteractive | ||
| ARG GO_VERSION=1.23.4 | ||
|
|
||
| # Install the Go toolchain from the official tarball — newer than Ubuntu | ||
| # 22.04's apt golang (1.18) and self-contained under /usr/local/go. | ||
| RUN curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" -o /tmp/go.tgz \ | ||
| && tar -C /usr/local -xzf /tmp/go.tgz \ | ||
| && rm /tmp/go.tgz \ | ||
| && /usr/local/go/bin/go version | ||
|
|
||
| ENV GOROOT=/usr/local/go \ | ||
| GOPATH=/go \ | ||
| PATH="/usr/local/go/bin:/go/bin:${PATH}" | ||
|
|
||
| WORKDIR /app | ||
| COPY main.go /app/main.go | ||
| COPY main_test.go /app/main_test.go | ||
|
|
||
| # Run unit tests (httptest-based, no network needed) before building. | ||
| RUN CGO_ENABLED=0 go test -v . | ||
|
|
||
| # Build a static-ish binary; CGO_ENABLED=0 avoids libc linkage surprises. | ||
| RUN CGO_ENABLED=0 go build -o /app/helloserver main.go | ||
|
|
||
| EXPOSE 8080 49983 | ||
|
|
||
| # cube-entrypoint.sh (inherited from the base image) will: | ||
| # 1. Launch envd on ${ENVD_PORT:-49983} in the background. | ||
| # 2. exec the CMD below as the foreground process, so the Go server owns | ||
| # PID 1's child and receives SIGTERM on `docker stop`. | ||
| CMD ["/app/helloserver"] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| # cubesandbox-base-go demo | ||
|
|
||
| A minimal image that stacks a **Go toolchain** and a tiny HTTP server on top of | ||
| [`cubesandbox-base`](../../docker/Dockerfile.cube-base), so you can test the | ||
| "Bring Your Own Image" flow end-to-end with a real Go workload — and use it as | ||
| a ready-made starting point for Go-based sandboxes. | ||
|
|
||
| - envd listens on `:49983` (Cube readiness probe) — inherited from the base image. | ||
| - A Go HTTP server (standard library `net/http`) listens on `:8080` and serves a | ||
| tiny landing page that echoes the Go runtime version, so you can eyeball that | ||
| Go really served the request. | ||
|
|
||
| See [Bring Your Own Image (envd)](../../docs/guide/tutorials/bring-your-own-image.md) | ||
| for the full tutorial, and [`../cubesandbox-base-nginx/`](../cubesandbox-base-nginx/) | ||
| for a sibling example that follows the exact same pattern with nginx. | ||
|
|
||
| ## What's inside | ||
|
|
||
| - **Go toolchain** — official tarball from `go.dev`, newer than Ubuntu 22.04's | ||
| apt `golang` (1.18). Installed under `/usr/local/go`, with `GOROOT`/`GOPATH`/`PATH` | ||
| wired up so `go` is on `$PATH`. | ||
| - **`main.go`** — a single-file HTTP server built on the standard library | ||
| `net/http` package, so the image needs **no third-party dependencies** and no | ||
| network access to build. Compiled to `/app/helloserver` at build time and | ||
| launched as the foreground process via `CMD ["/app/helloserver"]`. | ||
|
|
||
| ## Build | ||
|
|
||
| ```bash | ||
| docker build -t cubesandbox-demo-go:latest . | ||
| ``` | ||
|
|
||
| The Dockerfile runs `go test` during the build, so a failing unit test | ||
| will abort the image build. | ||
|
|
||
| > Pin a different Go version with `--build-arg GO_VERSION=1.22.0`. | ||
|
|
||
| ## Run unit tests locally | ||
|
|
||
| ```bash | ||
| go test -v . | ||
| ``` | ||
|
|
||
| Tests use only the standard library `net/http/httptest` — no external | ||
| dependencies or running server required. | ||
|
|
||
| ## Run & verify locally | ||
|
|
||
| ```bash | ||
| docker run --rm -d \ | ||
| -p 8080:8080 \ | ||
| -p 49983:49983 \ | ||
| --name cube-demo-go \ | ||
| cubesandbox-demo-go:latest | ||
|
|
||
| # Go server: should print the demo landing page (with the Go runtime version) | ||
| curl -s http://127.0.0.1:8080/ | ||
|
|
||
| # envd readiness probe: should return 204 | ||
| curl -s -o /dev/null -w "envd /health => %{http_code}\n" \ | ||
| http://127.0.0.1:49983/health | ||
|
|
||
| # Sanity-check the toolchain inside the container | ||
| docker exec cube-demo-go go version | ||
| docker exec cube-demo-go go env GOROOT GOPATH | ||
|
|
||
| docker rm -f cube-demo-go | ||
| ``` | ||
|
|
||
| ## Register as a Cube template | ||
|
|
||
| ```bash | ||
| cubemastercli tpl create-from-image \ | ||
| --image <your-registry>/cubesandbox-demo-go:latest \ | ||
| --writable-layer-size 1G \ | ||
| --expose-port 49983 \ | ||
| --expose-port 8080 \ | ||
| --probe 49983 \ | ||
| --probe-path /health | ||
| ``` | ||
|
|
||
| `--probe 49983 --probe-path /health` points Cube at envd (guaranteed to | ||
| return `204` within ~1s); the Go server's `:8080` stays exposed for your | ||
| actual traffic. See | ||
| [Creating Templates from OCI Images](../../docs/guide/tutorials/template-from-image.md) | ||
| for monitoring (`tpl watch`) and troubleshooting. | ||
|
|
||
| ## Try it with the E2B SDK | ||
|
|
||
| After registering the template, [`test_sandbox.py`](./test_sandbox.py) boots | ||
| a sandbox from it and verifies four things: | ||
|
|
||
| 1. `go version` runs inside the sandbox (toolchain is on `$PATH`) | ||
| 2. `go env GOROOT GOPATH` reports the expected paths | ||
| 3. `/app/main.go` is readable via `sandbox.files.read(...)` | ||
| 4. An HTTPS request to the sandbox's port `8080` returns the Go server's | ||
| landing page | ||
|
|
||
| ```bash | ||
| pip install -r requirements.txt | ||
|
|
||
| cp env.example .env | ||
| # fill in E2B_API_URL and CUBE_TEMPLATE_ID | ||
|
|
||
| python3 test_sandbox.py | ||
| ``` | ||
|
|
||
| ## Files | ||
|
|
||
| ``` | ||
| cubesandbox-base-go/ | ||
| ├── Dockerfile # FROM cubesandbox-base, installs Go toolchain, builds & runs the server | ||
| ├── main.go # Standard-library HTTP server (net/http), no third-party deps | ||
| ├── main_test.go # Unit tests for handleRoot / handleHealth (go test, no external deps) | ||
| ├── test_sandbox.py # E2B SDK smoke test: go version/env, file read, HTTP GET :8080 | ||
| ├── env.example # E2B_API_URL / E2B_API_KEY / CUBE_TEMPLATE_ID | ||
| ├── requirements.txt # e2b + python-dotenv | ||
| └── README.md # This file | ||
| ``` | ||
|
|
||
| ## Use cases | ||
|
|
||
| - **Go code execution sandbox** — boot a sandbox and run `go build` / `go test` | ||
| via `sandbox.commands.run(...)` for isolated build or test jobs. | ||
| - **Go Web service base** — replace `main.go` with your own Gin / Echo / chi / | ||
| vanilla `net/http` app and expose its port with `--expose-port`. | ||
| - **CGo / multi-module build runner** — clone a repo into the sandbox and run | ||
| `go build ./...` against an isolated, disposable environment. | ||
|
|
||
| ## Known limitations | ||
|
|
||
| - The image ships a **single Go version** (default `1.23.4`). Override with | ||
| `--build-arg GO_VERSION=<x.y.z>` for a different release. | ||
| - **`GOPATH` is `/go`** with no pre-created `src` tree. Go modules are the | ||
| default and recommended workflow; if you need legacy `GOPATH` mode, `mkdir -p | ||
| /go/src` in your downstream Dockerfile. | ||
| - `main.go` is a demo, not a production server — it has no TLS, no graceful | ||
| shutdown, no timeouts, and serves a single route. Swap it for your real | ||
| application's binary. | ||
| - The Go binary is built with `CGO_ENABLED=0` (pure Go). If your project needs | ||
| CGo (e.g. links a C library), drop that flag and ensure the toolchain image | ||
| has a C compiler (`apt-get install -y gcc`). | ||
|
|
||
| ## Related | ||
|
|
||
| - [`../cubesandbox-base-nginx/`](../cubesandbox-base-nginx/) — the sibling | ||
| example this template is modelled on (nginx instead of Go). | ||
| - [`../cubesandbox-base-java/`](../cubesandbox-base-java/) — another sibling, | ||
| Java 17 + Maven on the same base image. | ||
| - [Bring Your Own Image (envd)](../../docs/guide/tutorials/bring-your-own-image.md) | ||
| — the entrypoint contract and `envd` requirements. | ||
| - [Creating Templates from OCI Images](../../docs/guide/tutorials/template-from-image.md) | ||
| — full `cubemastercli tpl create-from-image` reference. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # Required: Cube API server address | ||
| export E2B_API_URL="http://<your-node-ip>:3000" | ||
|
|
||
| # Required: any non-empty value satisfies the SDK check | ||
| export E2B_API_KEY="e2b_000000" | ||
|
|
||
| # Required: template ID from `cubemastercli tpl create-from-image ...` | ||
| export CUBE_TEMPLATE_ID="<your-template-id>" | ||
|
|
||
|
|
||
| export SSL_CERT_FILE="/etc/pki/tls/cert.pem" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| // Minimal HTTP server used by the cubesandbox-base-go demo template. | ||
| // | ||
| // Uses only the Go standard library (net/http) so the image needs no | ||
| // third-party dependencies. Listens on :8080 (override via APP_PORT) and | ||
| // serves a tiny landing page plus a /health endpoint. The Cube readiness | ||
| // probe is served by envd on :49983 — this server is the "real" application | ||
| // traffic endpoint, mirroring how nginx serves :80 in cubesandbox-base-nginx. | ||
| package main | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "net/http" | ||
| "os" | ||
| "runtime" | ||
| ) | ||
|
|
||
| func main() { | ||
| port := os.Getenv("APP_PORT") | ||
| if port == "" { | ||
| port = "8080" | ||
| } | ||
|
|
||
| mux := http.NewServeMux() | ||
| mux.HandleFunc("/", handleRoot) | ||
| mux.HandleFunc("/health", handleHealth) | ||
|
|
||
| fmt.Printf("helloserver listening on :%s (Go %s)\n", port, runtime.Version()) | ||
| if err := http.ListenAndServe(":"+port, mux); err != nil { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing HTTP timeouts — srv := &http.Server{
Addr: ":" + port,
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 30 * time.Second,
}
if err := srv.ListenAndServe(); err != nil { ... }(This is a demo, so even 30s defaults are better than 0 = no timeout.) |
||
| fmt.Fprintln(os.Stderr, "server error:", err) | ||
| os.Exit(1) | ||
| } | ||
| } | ||
|
|
||
| func handleRoot(w http.ResponseWriter, r *http.Request) { | ||
| w.Header().Set("Content-Type", "text/html; charset=utf-8") | ||
| port := os.Getenv("APP_PORT") | ||
| if port == "" { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| port = "8080" | ||
| } | ||
| fmt.Fprintf(w, "<!doctype html>\n"+ | ||
| "<title>cubesandbox-base-go</title>\n"+ | ||
| "<h1>Hello from Go inside a CubeSandbox MicroVM</h1>\n"+ | ||
| "<p>Go runtime: %s</p>\n"+ | ||
| "<p>envd is running on :49983, this Go server on :%s.</p>\n", | ||
| runtime.Version(), port) | ||
| } | ||
|
|
||
| func handleHealth(w http.ResponseWriter, r *http.Request) { | ||
| w.WriteHeader(http.StatusOK) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: missing
go.mod—go test -v .will fail without ago.modfile because Go ≥1.16 defaults to module mode (GO111MODULE=on). Runninggo test .(package mode) requires a module context; onlygo build main.go(file-argument mode) works without one.Add either:
RUN go mod init cubesandbox-demo-gostep before the test command, orgo.modfile committed to the repository.(The standalone
go build -o /app/helloserver main.goon line 41 will work because it specifies a file argument, but the test will fail first.)