Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/commit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ jobs:

- name: "Cache Envoy binaries"
uses: actions/cache@v4
with: # ~/.func-e/versions is cached so that we only re-download once: for TestFuncEInstall
path: ~/.func-e/versions
with: # ~/.local/share/func-e/envoy-versions is cached so that we only re-download once: for TestFuncEInstall
path: ~/.local/share/func-e/envoy-versions
key: test-${{ runner.os }}-envoy-${{ hashFiles('internal/version/last_known_envoy.txt') }}
restore-keys: test-${{ runner.os }}-envoy-

Expand Down
58 changes: 58 additions & 0 deletions CONFIG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# func-e configuration

The [XDG Base Directory Specification][xdg] defines standard locations for
user-specific files:

- **Data files**: Persist across sessions, e.g. downloaded binaries
- **State files**: Persist between restarts but non-portable, e.g. stdout.log
- **Runtime files**: Ephemeral files, e.g. admin-address.txt

func-e adopts these conventions to separate downloaded Envoy binaries, logs,
and ephemeral admin addresses. Doing so allows library consumers like Envoy AI
Gateway to define their own home directories under an XDG base convention.

## Configuration mappings

| Environment Variable | Default Path | API Option |
|-----------------------|-------------------------|---------------------|
| `FUNC_E_CONFIG_HOME` | `~/.config/func-e` | `api.ConfigHome()` |
| `FUNC_E_DATA_HOME` | `~/.local/share/func-e` | `api.DataHome()` |
| `FUNC_E_STATE_HOME` | `~/.local/state/func-e` | `api.StateHome()` |
| `FUNC_E_RUNTIME_DIR` | `/tmp/func-e-${UID}` | `api.RuntimeDir()` |
| `FUNC_E_RUN_ID` | auto-generated | `api.RunID()` |

| File Type | Purpose | Default Path |
|------------------------|----------------------------------------------|----------------------------------------------------------------------------------|
| Selected Envoy Version | Version preference (persistent, shared) | `${FUNC_E_CONFIG_HOME}/envoy-version` |
| Envoy Binaries | Downloaded executables (persistent, shared) | `${FUNC_E_DATA_HOME}/envoy-versions/{version}/bin/envoy` |
| Envoy Run State | Per-run logs & config (persistent debugging) | `${FUNC_E_STATE_HOME}/envoy-runs/{runID}/stdout.log,stderr.log,config_dump.json` |
| Admin Address Default | Generated endpoint (ephemeral, per-run) | `${FUNC_E_RUNTIME_DIR}/{runID}/admin-address.txt` |

- **Correlation ID (`runID`)**: `YYYYMMDD_HHMMSS_UUU`
- (epoch date, time, last 3 digits of micros to allow concurrent runs)
- Can be customized via `FUNC_E_RUN_ID` environment variable or `--run-id` flag
- Custom runID cannot contain path separators (/ or \)
- Example: `--run-id 0` for predictable Docker/Kubernetes deployments
- **Directory per run** isolates concurrent runs and ensures correlation

## Legacy Mapping

**Deprecation Warning**:
```
WARNING: FUNC_E_HOME is deprecated and will be removed in a future version.
Please migrate to FUNC_E_CONFIG_HOME, FUNC_E_DATA_HOME, FUNC_E_STATE_HOME or FUNC_E_RUNTIME_DIR.
```

| File Type | Legacy Path Pattern | XDG Path Pattern |
|------------------------|-----------------------------------------------|-------------------------------------------------------------|
| Selected Envoy Version | `$FUNC_E_HOME/version` | `$FUNC_E_CONFIG_HOME/envoy-version` |
| Envoy Binaries | `$FUNC_E_HOME/versions/{version}/bin/envoy` | `$FUNC_E_DATA_HOME/envoy-versions/{version}/bin/envoy` |
| Run Logs | `$FUNC_E_HOME/runs/{epoch}/stdout.log` | `$FUNC_E_STATE_HOME/envoy-runs/{runID}/stdout.log` |
| Admin Address | `$FUNC_E_HOME/runs/{epoch}/admin-address.txt` | `$FUNC_E_RUNTIME_DIR/{runID}/admin-address.txt` |

These legacy patterns will be supported only when `FUNC_E_HOME` is set and will
be removed in a future version. A file envoy.pid will not be written as it
isn't necessary.

---
[xdg]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
28 changes: 28 additions & 0 deletions RATIONALE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,31 @@ much an emergency as if we put emulation in the critical path (ex in PR tests.)

At the point when GitHub Actions supports free arm64 runners, we can simplify by removing Travis.
Azure DevOps Pipelines already supports arm64, so it is possible GitHub Actions will in the future.

## Why do we have so many environment variables for file locations?

Recently, func-e is used as a library, which means its default locations need
to support layouts that are non-default. We also want to be able to predict the
log directory when running in Docker instead of using a timestamp in the path.

This results in several config locations like this:

| Environment Variable | Default Path | API Option |
|-----------------------|-------------------------|---------------------|
| `FUNC_E_CONFIG_HOME` | `~/.config/func-e` | `api.ConfigHome()` |
| `FUNC_E_DATA_HOME` | `~/.local/share/func-e` | `api.DataHome()` |
| `FUNC_E_STATE_HOME` | `~/.local/state/func-e` | `api.StateHome()` |
| `FUNC_E_RUNTIME_DIR` | `/tmp/func-e-${UID}` | `api.RuntimeDir()` |
| `FUNC_E_RUN_ID` | auto-generated | `api.RunID()` |

These are conventional to [XDG][xdg], which makes it easier to explain to
people. Also, XDS conventions are used by Prometheus and block/goose, so will
be familiar to some.

In summary, XDS conventions allow dependents like Envoy AI Gateway to brand
their own directories and co-mingle its configuration and logs with those
of func-e when it runs Envoy (the gateway process). It also allows Docker to
export `FUNC_E_RUN_ID=0` to aid in location of key files.

---
[xdg]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
23 changes: 19 additions & 4 deletions USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,28 @@ downloads and installs the latest version of Envoy for you.

To list versions of Envoy you can use, execute `func-e versions -a`. To
choose one, invoke `func-e use 1.35.3`. This installs into
`$FUNC_E_HOME/versions/1.35.3`, if not already present. You may also use
minor version, such as `func-e use 1.35`.
`$FUNC_E_DATA_HOME/envoy-versions/1.35.3`, if not already present. You may
also use minor version, such as `func-e use 1.35`.

You may want to override `$ENVOY_VERSIONS_URL` to supply custom builds or
otherwise control the source of Envoy binaries. When overriding, validate
your JSON first: https://archive.tetratelabs.io/release-versions-schema.json

Directory structure:
`$FUNC_E_CONFIG_HOME` stores configuration files
(default: ${HOME}/.config/func-e)
`$FUNC_E_DATA_HOME` stores Envoy binaries
(default: ${HOME}/.local/share/func-e)
`$FUNC_E_STATE_HOME` stores logs
(default: ${HOME}/.local/state/func-e)
`$FUNC_E_RUNTIME_DIR` stores temporary files
(default: /tmp/func-e-${UID})

Advanced:
`FUNC_E_PLATFORM` overrides the host OS and architecture of Envoy binaries.
This is used when emulating another platform, e.g. x86 on Apple Silicon M1.
Note: Changing the OS value can cause problems as Envoy has dependencies,
such as glibc. This value must be constant within a `$FUNC_E_HOME`.
such as glibc. This value must be constant within a `$FUNC_E_DATA_HOME`.

# Commands

Expand All @@ -32,6 +42,11 @@ such as glibc. This value must be constant within a `$FUNC_E_HOME`.

| Name | Usage | Default |
| ---- | ----- | ------- |
| FUNC_E_HOME | func-e home directory (location of installed versions and run archives) | ${HOME}/.func-e |
| FUNC_E_HOME | (deprecated) func-e home directory - use --config-home, --data-home, --state-home or --runtime-dir instead | |
| FUNC_E_CONFIG_HOME | directory for configuration files | ${HOME}/.config/func-e |
| FUNC_E_DATA_HOME | directory for Envoy binaries | ${HOME}/.local/share/func-e |
| FUNC_E_STATE_HOME | directory for logs (used by run command) | ${HOME}/.local/state/func-e |
| FUNC_E_RUNTIME_DIR | directory for temporary files (used by run command) | /tmp/func-e-${UID} |
| FUNC_E_RUN_ID | custom run identifier for logs/runtime directories (used by run command) | auto-generated timestamp |
| ENVOY_VERSIONS_URL | URL of Envoy versions JSON | https://archive.tetratelabs.io/envoy/envoy-versions.json |
| FUNC_E_PLATFORM | the host OS and architecture of Envoy binaries. Ex. darwin/arm64 | $GOOS/$GOARCH |
72 changes: 68 additions & 4 deletions api/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,75 @@ import (
"github.com/tetratelabs/func-e/internal/api"
)

// HomeDir is an absolute path which most importantly contains "versions"
// installed from EnvoyVersionsURL. Defaults to "${HOME}/.func-e"
// Deprecated: Use ConfigHome, DataHome, StateHome or RuntimeDir instead.
// This function will be removed in a future version.
func HomeDir(homeDir string) RunOption {
return func(o *api.RunOpts) {
o.HomeDir = homeDir
o.ConfigHome = homeDir
o.DataHome = homeDir
o.StateHome = homeDir
o.RuntimeDir = homeDir
}
}

// ConfigHome is the directory containing configuration files.
// Defaults to "~/.config/func-e"
//
// Files stored here:
// - envoy-version (selected version preference)
func ConfigHome(configHome string) RunOption {
return func(o *api.RunOpts) {
o.ConfigHome = configHome
}
}

// DataHome is the directory containing downloaded Envoy binaries.
// Defaults to "~/.local/share/func-e"
//
// Files stored here:
// - envoy-versions/{version}/bin/envoy (downloaded Envoy binaries)
func DataHome(dataHome string) RunOption {
return func(o *api.RunOpts) {
o.DataHome = dataHome
}
}

// StateHome is the directory containing persistent state like run logs.
// Defaults to "~/.local/state/func-e"
//
// Files stored here:
// - envoy-runs/{runID}/stdout.log,stderr.log (per-run logs)
// - envoy-runs/{runID}/config_dump.json (Envoy configuration snapshot)
func StateHome(stateHome string) RunOption {
return func(o *api.RunOpts) {
o.StateHome = stateHome
}
}

// RuntimeDir is the directory containing ephemeral runtime files.
// Defaults to "/tmp/func-e-${UID}"
//
// Files stored here:
// - {runID}/admin-address.txt (Envoy admin API endpoint)
//
// Note: Runtime files are ephemeral and may be cleaned up on system restart.
func RuntimeDir(runtimeDir string) RunOption {
return func(o *api.RunOpts) {
o.RuntimeDir = runtimeDir
}
}

// RunID sets a custom run identifier used in StateDir and RuntimeDir paths.
// By default, a timestamp-based runID is auto-generated (e.g., "20250115_123456_789").
//
// Use this to:
// - Create predictable directories for Docker/K8s (e.g., RunID("0"))
// - Implement custom naming schemes
//
// Validation: runID cannot contain path separators (/ or \)
func RunID(runID string) RunOption {
return func(o *api.RunOpts) {
o.RunID = runID
}
}

Expand All @@ -29,7 +93,7 @@ func EnvoyVersionsURL(envoyVersionsURL string) RunOption {
}

// EnvoyVersion overrides the version of Envoy to run. Defaults to the
// contents of "$HomeDir/versions/version".
// contents of "$ConfigHome/envoy-version".
//
// When that file is missing, it is generated from ".latestVersion" from the
// EnvoyVersionsURL. Its value can be in full version major.minor.patch format,
Expand Down
7 changes: 5 additions & 2 deletions e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ go test -parallel 1 -v -failfast ./e2e
When run via `make`, `func-e` is built on-demand by `$(current_binary)` target (same as `make build`).
Ex. `$PWD/build/func-e_darwin_arm64/func-e`

It is also a good idea to override `FUNC_E_HOME` when running e2e, since by default it uses `$HOME/.func-e`.
It is also a good idea to override the data directories when running e2e to avoid interfering with your local environment.
The defaults are `~/.local/share/func-e`, `~/.local/state/func-e` and `/tmp/func-e-${UID}`.

```bash
FUNC_E_HOME=/tmp/test make e2e
FUNC_E_DATA_HOME=/tmp/test FUNC_E_STATE_HOME=/tmp/test FUNC_E_RUNTIME_DIR=/tmp/test make e2e
```

Note: The deprecated `FUNC_E_HOME` environment variable is still supported for backwards compatibility.

## Envoy version list
If the `func-e` version is a snapshot and "envoy-versions.json" exists, tests run against the local. This allows local
development and pull requests to verify changes not yet [published](https://archive.tetratelabs.io/envoy/envoy-versions.json)
Expand Down
12 changes: 10 additions & 2 deletions e2e/func-e_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ func TestRun_LogWarn(t *testing.T) {
e2e.TestRun_LogWarn(t.Context(), t, funcEFactory{})
}

func TestRun_RunDirectory(t *testing.T) {
e2e.TestRun_RunDirectory(t.Context(), t, funcEFactory{})
func TestRun_RunDir(t *testing.T) {
// For binary e2e tests, state directory is controlled via FUNC_E_STATE_HOME env var
// (CLI layer), not library API options like api.StateHome().
stateDir := t.TempDir()
t.Setenv("FUNC_E_STATE_HOME", stateDir)
e2e.TestRun_RunDir(t.Context(), t, funcEFactory{}, stateDir)
}

func TestRun_InvalidConfig(t *testing.T) {
Expand All @@ -36,3 +40,7 @@ func TestRun_StaticFile(t *testing.T) {
func TestRun_CtrlCs(t *testing.T) {
e2e.TestRun_CtrlCs(t.Context(), t, funcEFactory{})
}

func TestRun_LegacyHomeDir(t *testing.T) {
e2e.TestRun_LegacyHomeDir(t.Context(), t, funcEFactory{})
}
25 changes: 20 additions & 5 deletions e2e/func-e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ import (
"time"

"github.com/tetratelabs/func-e/experimental/admin"
internaladmin "github.com/tetratelabs/func-e/internal/admin"
"github.com/tetratelabs/func-e/internal/test/build"
"github.com/tetratelabs/func-e/internal/test/e2e"
internale2e "github.com/tetratelabs/func-e/internal/test/e2e"
)

var (
Expand Down Expand Up @@ -72,17 +73,24 @@ func funcEExec(ctx context.Context, args ...string) (string, string, error) {
return stdout.String(), stderr.String(), err
}

// funcEFactory implements runtest.FuncEFactory for E2E tests using a compiled func-e binary.
// funcEFactory implements internale2e.FuncEFactory for E2E tests using a
// compiled func-e binary.
type funcEFactory struct{}

func (funcEFactory) New(_ context.Context, _ *testing.T, stdout, stderr io.Writer) (e2e.FuncE, error) {
func (funcEFactory) New(_ context.Context, _ *testing.T, stdout, stderr io.Writer) (internale2e.FuncE, error) {
return &funcE{stdout: stdout, stderr: stderr}, nil
}

// funcE implements runtest.FuncE for e2e tests using the compiled binary
// funcE implements internale2e.FuncE for e2e tests using the compiled binary
type funcE struct {
cmd *exec.Cmd
stdout, stderr io.Writer
envoyPid int
}

// EnvoyPid implements the same method as documented on internale2e.FuncE
func (a *funcE) EnvoyPid() int {
return a.envoyPid
}

// OnStart inspects the running func-e process tree to find the Envoy process and its run directory,
Expand All @@ -93,7 +101,14 @@ func (a *funcE) OnStart(ctx context.Context) (admin.AdminClient, error) {
}
funcEPid := a.cmd.Process.Pid

adminClient, err := admin.NewAdminClient(ctx, funcEPid)
// Poll for the admin address path from the Envoy process command line
envoyPid, adminAddressPath, err := internaladmin.PollEnvoyPidAndAdminAddressPath(ctx, funcEPid)
if err != nil {
return nil, err
}
a.envoyPid = envoyPid

adminClient, err := internaladmin.NewAdminClient(ctx, adminAddressPath)
if err == nil {
err = adminClient.AwaitReady(ctx, 100*time.Millisecond)
}
Expand Down
Loading