Documentation
¶
Overview ¶
Package builder owns the async template build runtime — translator from SDK step arrays into generated Dockerfiles, file-context staging, and the BuildManager state machine that drives docker build to completion.
The E2B JS SDK serializes a programmatic template into a small JSON wire format with exactly five step types — COPY, RUN, WORKDIR, USER, ENV — plus two out-of-band fields (startCmd, readyCmd) that the sandbox manager, not the Dockerfile, consumes. Everything else the SDK's fluent API exposes (aptInstall, pipInstall, makeDir, rename, remove, gitClone, …) compiles down to a RUN step client-side before it reaches us. See test/e2e/ts/node_modules/e2b/dist/index.mjs:5485-5646 for the full list.
This package is pure — no Docker, no HTTP. The Manager in manager.go composes it with a runtime.Runtime executor.
Index ¶
- Constants
- Variables
- func PrepareContext(cache *filecache.Cache, buildDir, stagingDir string, hashes []string) error
- type BuildResult
- type DockerExecutor
- type EnqueueSpec
- type Executor
- type ExecutorSpec
- type Input
- type LogEntry
- type LogSink
- type Manager
- func (m *Manager) Cancel(buildID string) error
- func (m *Manager) Enqueue(ctx context.Context, req EnqueueSpec) error
- func (m *Manager) Logs(buildID string, offset int64, limit int) ([]LogEntry, int64, error)
- func (m *Manager) Status(buildID string) (Status, error)
- func (m *Manager) Wait(ctx context.Context, buildID string) error
- type ManagerOptions
- type Output
- type Status
Constants ¶
const DefaultEnvdInitPath = "/usr/local/bin/edvabe-init"
DefaultEnvdInitPath is the path we place the edvabe-init wrapper at inside the final image. The wrapper launches envd in the background and then the user's startCmd, so both live alongside each other for the lifetime of the sandbox.
const EnvdSourceImage = "edvabe/envd-source:latest"
EnvdSourceImage is the scratch image edvabe builds once at startup that holds the envd binary and the edvabe-init wrapper. Every generated Dockerfile appends a final stage that copies from this image so user templates do not need to install envd themselves.
Variables ¶
var ErrBuildNotFound = errors.New("builder: build not found")
ErrBuildNotFound is returned by Status/Logs when the given buildID is unknown to the manager.
Functions ¶
func PrepareContext ¶
PrepareContext extracts the file-cache blob for each hash in hashes into buildDir/<StagingDir>/<hash>/ so the Dockerfile that references those paths can be fed to docker build.
SDK tars are gzipped (see tarFileStream in test/e2e/ts/node_modules/e2b/dist/index.mjs:4580-4596). We accept both gzipped and uncompressed tars transparently; anything else is an error.
If a hash is already extracted (directory exists and is non-empty), the call is a no-op for that hash. buildDir is assumed to exist. stagingDir is the same value passed to Translate.Input.StagingDir.
Types ¶
type BuildResult ¶
type BuildResult struct {
TemplateID string
BuildID string
Status template.BuildStatus
Reason string
}
BuildResult is passed to ManagerOptions.OnComplete after a build finishes (successfully or not).
type DockerExecutor ¶
type DockerExecutor struct {
// Runtime is the docker runtime. Required.
Runtime runtime.Runtime
// Cache is the content-addressed file cache the SDK uploaded
// tarred step contexts into. Required whenever any step carries a
// filesHash — pure-RUN templates work with a nil cache.
Cache *filecache.Cache
// BuildRoot is the parent directory under which per-build scratch
// dirs are created. Required; must exist (or be createable).
BuildRoot string
// StagingDir is the name of the directory, inside each per-build
// scratch dir, where extracted file contexts land. Defaults to
// "ctx". Must match the value used by Translate.
StagingDir string
// Keep, when true, leaves the scratch dir on disk after the build
// completes. Used by tests to inspect the generated Dockerfile and
// staged files.
Keep bool
}
DockerExecutor binds the Manager's Executor contract to the real docker build pipeline: extract cached tar contexts, translate the step array into a Dockerfile, write it into a build dir, and hand off to runtime.Runtime.BuildImage with a streaming log sink.
Build scratch lives under BuildRoot/<buildID>/; the directory is removed when the build finishes (success or failure) unless Keep is set, which the integration tests use to inspect the generated context.
func (*DockerExecutor) Run ¶
func (e *DockerExecutor) Run(ctx context.Context, spec ExecutorSpec, sink LogSink) error
Run implements builder.Executor.
type EnqueueSpec ¶
type EnqueueSpec struct {
TemplateID string
BuildID string
Spec template.BuildSpec
ParentImage string
}
EnqueueSpec carries the build parameters in from the HTTP handler. ParentImage is pre-resolved by the caller when Spec.FromTemplate is set; the Manager itself does not touch the template store.
type Executor ¶
type Executor interface {
// Run executes one build. Implementations stream log lines into
// sink as they occur. Returns nil on success (the image is ready
// at ResultImageTag), or an error describing the failure.
Run(ctx context.Context, spec ExecutorSpec, sink LogSink) error
}
Executor is the abstraction the Manager uses to actually run a build. Separating it from the Manager keeps the state machine pure Go and unit-testable without Docker — tests pass a fake executor that emits canned log lines and completes or fails on cue.
type ExecutorSpec ¶
type ExecutorSpec struct {
TemplateID string
BuildID string
ResultImage string
Spec template.BuildSpec
ParentImage string // resolved tag when Spec.FromTemplate is set
}
ExecutorSpec is everything an Executor needs to run one build. The Manager composes it from the BuildSpec sent by the SDK plus the resolved template metadata.
type Input ¶
type Input struct {
// FromImage is the base image for the generated Dockerfile. Either
// this or FromTemplateImage must be set, not both.
FromImage string
// FromTemplateImage is the resolved image tag of a parent template
// (set by the caller after looking up TemplateBuildStartV2.fromTemplate
// against the template store). Mutually exclusive with FromImage.
FromTemplateImage string
// Steps is the ordered SDK step list as received on the wire.
Steps []template.Step
// StagingDir is the relative path, inside the docker build
// context, where extracted file contexts live. Each step's
// filesHash gets extracted into <StagingDir>/<hash>/ so that
// the generated COPY lines can reference them. Defaults to "ctx"
// if empty.
StagingDir string
}
Input is the translator's sole argument. All fields are required except where noted; the zero value is not usable.
type LogEntry ¶
type LogEntry struct {
Timestamp time.Time `json:"timestamp"`
Level string `json:"level"`
Source string `json:"source,omitempty"`
Message string `json:"message"`
}
LogEntry is one structured line from a docker build stream. The fields mirror what the E2B SDK's TemplateBuildLogEntry expects (timestamp, level, message). Source is included so we can tag lines from stderr vs stdout but the SDK ignores it when filtering.
type LogSink ¶
type LogSink interface {
Append(LogEntry)
}
LogSink is what the Executor writes log lines to. The Manager's implementation appends to the per-build ring buffer.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager drives the async template build lifecycle: enqueue → building → ready|error, with per-build log ring buffers. Operates on top of a pluggable Executor so tests can drive it without touching Docker.
func NewManager ¶
func NewManager(opts ManagerOptions) (*Manager, error)
NewManager constructs a Manager. Executor is required.
func (*Manager) Cancel ¶
Cancel signals a build's context so the executor can abort. The build is marked error with a "cancelled" reason once its goroutine observes the signal.
func (*Manager) Enqueue ¶
func (m *Manager) Enqueue(ctx context.Context, req EnqueueSpec) error
Enqueue registers a new build, starts its goroutine, and returns immediately. The build transitions synchronously through waiting → building before Enqueue returns, so a status poll immediately after will see "building" (not "waiting"). Duplicate enqueues for the same buildID are rejected.
func (*Manager) Logs ¶
Logs reads log entries from a build starting at offset, up to limit entries. Returns the entries and the next cursor. Stale cursors (pointing at entries already evicted from the ring) are snapped forward to the earliest still-held entry.
type ManagerOptions ¶
type ManagerOptions struct {
Executor Executor
// ResultImageFormat is the format string used to derive the final
// image tag from the templateID. Defaults to
// "edvabe/user-%s:latest".
ResultImageFormat string
// LogCapacity caps each build's ring buffer. Default 5000.
LogCapacity int
// Clock is injected for deterministic timestamps in tests.
Clock func() time.Time
// OnComplete is called after a build finishes. Use it to persist
// the build status back to the template store.
OnComplete func(BuildResult)
}
ManagerOptions configures NewManager.
type Output ¶
type Output struct {
// Dockerfile is the generated Dockerfile as a single string. It
// ends with a newline.
Dockerfile string
// RequiredFileHashes is the deduplicated list of filesHash values
// referenced by COPY steps. The BuildManager uses this to stage
// file contexts from the cache before invoking docker build.
RequiredFileHashes []string
}
Output is the translator's result.
func Translate ¶
Translate converts an SDK step array into a Dockerfile. The output Dockerfile has three sections:
- FROM <base>
- one or more lines per step, in order
- an envd injection tail that copies envd + edvabe-init from the EnvdSourceImage and rewrites CMD to the edvabe-init wrapper
startCmd / readyCmd are *not* emitted into the Dockerfile — they travel through the sandbox manager as EDVABE_START_CMD / EDVABE_READY_CMD environment variables at container create time.
type Status ¶
type Status struct {
TemplateID string `json:"templateID"`
BuildID string `json:"buildID"`
Status template.BuildStatus `json:"status"`
Reason string `json:"reason,omitempty"`
ResultTag string `json:"resultTag,omitempty"`
StartedAt time.Time `json:"startedAt"`
FinishedAt *time.Time `json:"finishedAt,omitempty"`
}
Status is a snapshot of a build's current state. The shape matches the wire response the SDK reads.