Skip to content

Commit 13a35db

Browse files
authored
feat: Make config file path configurable (#85)
1 parent 04ef7aa commit 13a35db

File tree

5 files changed

+32
-65
lines changed

5 files changed

+32
-65
lines changed

docs/development.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ touch config.json && echo '<json-config-content>' > config.json
1313
sudo mv config.json /etc/n8n-task-runners.json
1414
```
1515

16+
Alternatively, use this environment variable to specify the config file path:
17+
18+
```sh
19+
export N8N_RUNNERS_CONFIG_PATH=/path/to/your/config.json
20+
```
21+
1622
3. Make your changes.
1723

1824
4. Build launcher:

docs/setup.md

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -44,49 +44,10 @@ sequenceDiagram
4444

4545
## Config file
4646

47-
Example config file at `/etc/n8n-task-runners.json`:
48-
49-
```json
50-
{
51-
"task-runners": [
52-
{
53-
"runner-type": "javascript",
54-
"workdir": "/usr/local/bin",
55-
"command": "/usr/local/bin/node",
56-
"args": [
57-
"--disallow-code-generation-from-strings",
58-
"--disable-proto=delete",
59-
"/usr/local/lib/node_modules/n8n/node_modules/@n8n/task-runner/dist/start.js"
60-
],
61-
"health-check-server-port": "5681",
62-
"allowed-env": ["PATH", "GENERIC_TIMEZONE"],
63-
"env-overrides": {
64-
"N8N_RUNNERS_TASK_TIMEOUT": "80",
65-
"N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT": "120",
66-
"N8N_RUNNERS_MAX_CONCURRENCY": "3",
67-
"NODE_FUNCTION_ALLOW_BUILTIN": "crypto",
68-
"NODE_FUNCTION_ALLOW_EXTERNAL": "moment",
69-
"NODE_OPTIONS": "--max-old-space-size=4096"
70-
}
71-
},
72-
{
73-
"runner-type": "python",
74-
"workdir": "/usr/local/bin",
75-
"command": "/usr/local/bin/python",
76-
"args": [
77-
"/usr/local/lib/python3.13/site-packages/n8n/task-runner-python/main.py"
78-
],
79-
"health-check-server-port": "5682",
80-
"allowed-env": ["PATH", "GENERIC_TIMEZONE"],
81-
"env-overrides": {
82-
"N8N_RUNNERS_TASK_TIMEOUT": "30",
83-
"N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT": "30",
84-
"N8N_RUNNERS_MAX_CONCURRENCY": "2"
85-
}
86-
}
87-
]
88-
}
89-
```
47+
The launcher reads its config file from `/etc/n8n-task-runners.json` by default, or from the file path specified by the `N8N_RUNNERS_CONFIG_PATH` environment variable.
48+
49+
For an example, refer to the [config file](https://github.com/n8n-io/n8n/blob/master/docker/images/runners/n8n-task-runners.json) used in the [`n8nio/runners`](https://hub.docker.com/r/n8nio/runners) Docker image.
50+
9051

9152
| Property | Description |
9253
| --------------- | ----------------------------------------------------------------------------------------------------------------------- |
@@ -95,8 +56,8 @@ Example config file at `/etc/n8n-task-runners.json`:
9556
| `command` | Command to start the task runner. |
9657
| `args` | Args and flags to use with `command`. |
9758
| `health-check-server-port` | Port for the runner's health check server. When a single runner is configured, this is optional and defaults to `5681`. When multiple runners are configured, this is required and must be unique per runner.
98-
| `allowed-env` | Env vars that the launcher will pass through from its own environment to the runner. See [environment](environment.md). |
99-
| `env-overrides` | Env vars that the launcher will set directly on the runner. See [environment](environment.md). |
59+
| `allowed-env` | Env vars that the launcher will pass through from its own environment to the runner. See [environment variables](#environment-variables).
60+
| `env-overrides` | Env vars that the launcher will set directly on the runner. See [environment variables](#environment-variables).
10061

10162
## Environment variables
10263

internal/config/config.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ import (
1313
"github.com/sethvargo/go-envconfig"
1414
)
1515

16-
var configPath = "/etc/n8n-task-runners.json"
17-
1816
const (
1917
// EnvVarHealthCheckPort is the env var for the port for the launcher's health check server.
2018
EnvVarHealthCheckPort = "N8N_RUNNERS_LAUNCHER_HEALTH_CHECK_PORT"
@@ -52,6 +50,9 @@ type BaseConfig struct {
5250
// RunnerHealthCheckServerHost is the host for all runners' health check servers.
5351
RunnerHealthCheckServerHost string `env:"N8N_RUNNERS_HEALTH_CHECK_SERVER_HOST, default=127.0.0.1"`
5452

53+
// ConfigPath is the path to the runners config file. Default: `/etc/n8n-task-runners.json`.
54+
ConfigPath string `env:"N8N_RUNNERS_CONFIG_PATH, default=/etc/n8n-task-runners.json"`
55+
5556
// Sentry is the Sentry config for the launcher, a subset of what is defined in:
5657
// https://docs.sentry.io/platforms/go/configuration/options/
5758
Sentry *SentryConfig
@@ -92,7 +93,7 @@ type RunnerConfig struct {
9293
}
9394

9495
// LoadLauncherConfig loads the launcher's base config from the launcher's environment and
95-
// loads runner configs from the config file at `/etc/n8n-task-runners.json`.
96+
// loads runner configs from the config file specified by N8N_RUNNERS_CONFIG_PATH.
9697
func LoadLauncherConfig(runnerTypes []string, lookuper envconfig.Lookuper) (*LauncherConfig, error) {
9798
ctx := context.Background()
9899

@@ -131,7 +132,7 @@ func LoadLauncherConfig(runnerTypes []string, lookuper envconfig.Lookuper) (*Lau
131132

132133
// runners
133134

134-
runnerConfigs, err := readLauncherConfigFile(runnerTypes)
135+
runnerConfigs, err := readLauncherConfigFile(baseConfig.ConfigPath, runnerTypes)
135136
if err != nil {
136137
cfgErrs = append(cfgErrs, err)
137138
}
@@ -146,9 +147,10 @@ func LoadLauncherConfig(runnerTypes []string, lookuper envconfig.Lookuper) (*Lau
146147
}, nil
147148
}
148149

149-
// readLauncherConfigFile reads the config file at `/etc/n8n-task-runners.json` and
150+
// readLauncherConfigFile reads the config file at the specified path and
150151
// returns the runner config(s) for the requested runner type(s).
151-
func readLauncherConfigFile(runnerTypes []string) (map[string]*RunnerConfig, error) {
152+
func readLauncherConfigFile(configPath string, runnerTypes []string) (map[string]*RunnerConfig, error) {
153+
// #nosec G304 -- configPath is controlled by system administrator via environment variable
152154
data, err := os.ReadFile(configPath)
153155
if err != nil {
154156
return nil, fmt.Errorf("failed to open config file at %s: %v", configPath, err)
@@ -164,7 +166,7 @@ func readLauncherConfigFile(runnerTypes []string) (map[string]*RunnerConfig, err
164166
taskRunnersNum := len(fileConfig.TaskRunners)
165167

166168
if taskRunnersNum == 0 {
167-
return nil, errs.ErrMissingRunnerConfig
169+
return nil, fmt.Errorf("config file at %s contains no task runners", configPath)
168170
}
169171

170172
runnerConfigs := make(map[string]*RunnerConfig)

internal/config/config_test.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func TestLoadConfig(t *testing.T) {
3737
envVars: map[string]string{
3838
"N8N_RUNNERS_AUTH_TOKEN": "test-token",
3939
"N8N_RUNNERS_TASK_BROKER_URI": "http://localhost:5679",
40+
"N8N_RUNNERS_CONFIG_PATH": testConfigPath,
4041
"SENTRY_DSN": "https://[email protected]/123",
4142
},
4243
runnerType: "javascript",
@@ -48,6 +49,7 @@ func TestLoadConfig(t *testing.T) {
4849
envVars: map[string]string{
4950
"N8N_RUNNERS_AUTH_TOKEN": "test-token",
5051
"N8N_RUNNERS_TASK_BROKER_URI": "http://127.0.0.1:5679",
52+
"N8N_RUNNERS_CONFIG_PATH": testConfigPath,
5153
"SENTRY_DSN": "https://[email protected]/123",
5254
},
5355
runnerType: "javascript",
@@ -57,9 +59,7 @@ func TestLoadConfig(t *testing.T) {
5759

5860
for _, tt := range tests {
5961
t.Run(tt.name, func(t *testing.T) {
60-
configPath = testConfigPath
61-
62-
err := os.WriteFile(configPath, []byte(tt.configContent), 0600)
62+
err := os.WriteFile(testConfigPath, []byte(tt.configContent), 0600)
6363
require.NoError(t, err, "Failed to write test config file")
6464

6565
lookuper := envconfig.MapLookuper(tt.envVars)
@@ -93,17 +93,19 @@ func TestConfigFileErrors(t *testing.T) {
9393
envVars: map[string]string{
9494
"N8N_RUNNERS_AUTH_TOKEN": "test-token",
9595
"N8N_RUNNERS_TASK_BROKER_URI": "http://localhost:5679",
96+
"N8N_RUNNERS_CONFIG_PATH": testConfigPath,
9697
},
9798
},
9899
{
99100
name: "empty task runners array",
100101
configContent: `{
101102
"task-runners": []
102103
}`,
103-
expectedError: "found no task runner configs",
104+
expectedError: "contains no task runners",
104105
envVars: map[string]string{
105106
"N8N_RUNNERS_AUTH_TOKEN": "test-token",
106107
"N8N_RUNNERS_TASK_BROKER_URI": "http://localhost:5679",
108+
"N8N_RUNNERS_CONFIG_PATH": testConfigPath,
107109
},
108110
},
109111
{
@@ -121,16 +123,15 @@ func TestConfigFileErrors(t *testing.T) {
121123
envVars: map[string]string{
122124
"N8N_RUNNERS_AUTH_TOKEN": "test-token",
123125
"N8N_RUNNERS_TASK_BROKER_URI": "http://localhost:5679",
126+
"N8N_RUNNERS_CONFIG_PATH": testConfigPath,
124127
},
125128
},
126129
}
127130

128131
for _, tt := range tests {
129132
t.Run(tt.name, func(t *testing.T) {
130-
configPath = testConfigPath
131-
132133
if tt.configContent != "" {
133-
err := os.WriteFile(configPath, []byte(tt.configContent), 0600)
134+
err := os.WriteFile(testConfigPath, []byte(tt.configContent), 0600)
134135
require.NoError(t, err, "Failed to write test config file")
135136
}
136137

@@ -250,11 +251,11 @@ func TestBackwardsCompatibilityPortDefaults(t *testing.T) {
250251

251252
for _, tt := range tests {
252253
t.Run(tt.name, func(t *testing.T) {
253-
configPath = filepath.Join(t.TempDir(), "test-config.json")
254-
err := os.WriteFile(configPath, []byte(tt.configContent), 0600)
254+
testConfigPath := filepath.Join(t.TempDir(), "test-config.json")
255+
err := os.WriteFile(testConfigPath, []byte(tt.configContent), 0600)
255256
require.NoError(t, err)
256257

257-
configs, err := readLauncherConfigFile(tt.runnerTypes)
258+
configs, err := readLauncherConfigFile(testConfigPath, tt.runnerTypes)
258259

259260
if tt.expectError {
260261
assert.Error(t, err)

internal/errs/errs.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,4 @@ var (
1414

1515
// ErrNegativeAutoShutdownTimeout is returned when the auto shutdown timeout is a negative integer.
1616
ErrNegativeAutoShutdownTimeout = errors.New("negative auto-shutdown timeout - N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT must be >= 0")
17-
18-
// ErrMissingRunnerConfig is returned when the config file does not contain any runner configs.
19-
ErrMissingRunnerConfig = errors.New("found no task runner configs at /etc/n8n-task-runners.json")
2017
)

0 commit comments

Comments
 (0)