Rationalize default configuration file paths and update documentation
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
This commit is contained in:
@@ -137,7 +137,7 @@ For subprocess orchestration guidance, see [`docs/subprocess-operations.md`](doc
|
|||||||
|
|
||||||
Precedence:
|
Precedence:
|
||||||
1. defaults
|
1. defaults
|
||||||
2. config file (`--config`, `AUDITA_CONFIG`, or `/etc/audita/config.yml` when present)
|
2. config file (`--config`, `AUDITA_CONFIG`, or default search paths when present: `/usr/local/etc/audita/config.yml`, then `/etc/audita/config.yml`)
|
||||||
3. environment (`AUDITA_*`)
|
3. environment (`AUDITA_*`)
|
||||||
4. CLI flags
|
4. CLI flags
|
||||||
|
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ audita config print-effective [--config <config.yml>]
|
|||||||
Current runtime flow (`internal/cli/run.go`):
|
Current runtime flow (`internal/cli/run.go`):
|
||||||
1. Build runtime config from:
|
1. Build runtime config from:
|
||||||
- defaults;
|
- defaults;
|
||||||
- file config source (`--config`, `AUDITA_CONFIG`, or `/etc/audita/config.yml` when present);
|
- file config source (`--config`, `AUDITA_CONFIG`, or default search paths when present: `/usr/local/etc/audita/config.yml`, then `/etc/audita/config.yml`);
|
||||||
- environment overrides;
|
- environment overrides;
|
||||||
- CLI overrides.
|
- CLI overrides.
|
||||||
2. Parse flags and apply CLI overrides.
|
2. Parse flags and apply CLI overrides.
|
||||||
@@ -304,9 +304,9 @@ File-config source behavior:
|
|||||||
- required to exist, otherwise process fails clearly.
|
- required to exist, otherwise process fails clearly.
|
||||||
- `AUDITA_CONFIG` (when `--config` is not provided):
|
- `AUDITA_CONFIG` (when `--config` is not provided):
|
||||||
- required to exist, otherwise process fails clearly.
|
- required to exist, otherwise process fails clearly.
|
||||||
- default path `/etc/audita/config.yml` (when neither explicit source is provided):
|
- default paths `/usr/local/etc/audita/config.yml`, then `/etc/audita/config.yml` (when neither explicit source is provided):
|
||||||
- used only when present;
|
- first existing path in that order is used;
|
||||||
- silently ignored when missing.
|
- both missing is silently ignored.
|
||||||
|
|
||||||
Versioned file-config behavior (`internal/core/config/file_config.go`):
|
Versioned file-config behavior (`internal/core/config/file_config.go`):
|
||||||
- supported version: `version: 1`;
|
- supported version: `version: 1`;
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ Precedence for `audita process`:
|
|||||||
Config source behavior:
|
Config source behavior:
|
||||||
- `--config <path>`: missing path is a clear failure
|
- `--config <path>`: missing path is a clear failure
|
||||||
- `AUDITA_CONFIG`: missing path is a clear failure
|
- `AUDITA_CONFIG`: missing path is a clear failure
|
||||||
- default `/etc/audita/config.yml`: missing file is non-fatal
|
- defaults `/usr/local/etc/audita/config.yml`, then `/etc/audita/config.yml`: both missing is non-fatal
|
||||||
|
|
||||||
## Supported transcript input forms
|
## Supported transcript input forms
|
||||||
|
|
||||||
|
|||||||
@@ -26,13 +26,14 @@ For `audita process`, config path resolution is:
|
|||||||
|
|
||||||
1. `--config <path>` if provided
|
1. `--config <path>` if provided
|
||||||
2. `AUDITA_CONFIG` if set and `--config` is not provided
|
2. `AUDITA_CONFIG` if set and `--config` is not provided
|
||||||
3. default `/etc/audita/config.yml` if present
|
3. default `/usr/local/etc/audita/config.yml` if present
|
||||||
|
4. fallback default `/etc/audita/config.yml` if present
|
||||||
|
|
||||||
Missing-file behavior:
|
Missing-file behavior:
|
||||||
|
|
||||||
- missing `--config` path: hard failure;
|
- missing `--config` path: hard failure;
|
||||||
- missing `AUDITA_CONFIG` path: hard failure;
|
- missing `AUDITA_CONFIG` path: hard failure;
|
||||||
- missing `/etc/audita/config.yml`: non-fatal, run continues.
|
- missing both default-path files: non-fatal, run continues.
|
||||||
|
|
||||||
## Precedence model
|
## Precedence model
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ Use this checklist before cutting a pre-1.0 or 1.0 release candidate.
|
|||||||
- `audita config print-effective --config <path>`
|
- `audita config print-effective --config <path>`
|
||||||
- Confirm precedence behavior:
|
- Confirm precedence behavior:
|
||||||
- defaults -> file config -> environment -> CLI.
|
- defaults -> file config -> environment -> CLI.
|
||||||
- Confirm missing `/etc/audita/config.yml` is non-fatal when `--config`/`AUDITA_CONFIG` are unset.
|
- Confirm default config search order:
|
||||||
|
- `/usr/local/etc/audita/config.yml` first, then `/etc/audita/config.yml`.
|
||||||
|
- Confirm missing both default-path config files is non-fatal when `--config`/`AUDITA_CONFIG` are unset.
|
||||||
|
|
||||||
## Output schema checks
|
## Output schema checks
|
||||||
|
|
||||||
|
|||||||
@@ -982,13 +982,15 @@ func findConfigPathOverride(args []string) (path string, set bool, err error) {
|
|||||||
return "", false, nil
|
return "", false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var statConfigPath = os.Stat
|
||||||
|
|
||||||
func resolveConfigPath(cliConfigPath string, cliConfigPathSet bool, lookup func(string) (string, bool)) (path string, source string, err error) {
|
func resolveConfigPath(cliConfigPath string, cliConfigPathSet bool, lookup func(string) (string, bool)) (path string, source string, err error) {
|
||||||
if cliConfigPathSet {
|
if cliConfigPathSet {
|
||||||
path = strings.TrimSpace(cliConfigPath)
|
path = strings.TrimSpace(cliConfigPath)
|
||||||
if path == "" {
|
if path == "" {
|
||||||
return "", "", fmt.Errorf("--config requires a non-empty path")
|
return "", "", fmt.Errorf("--config requires a non-empty path")
|
||||||
}
|
}
|
||||||
if _, statErr := os.Stat(path); statErr != nil {
|
if _, statErr := statConfigPath(path); statErr != nil {
|
||||||
if os.IsNotExist(statErr) {
|
if os.IsNotExist(statErr) {
|
||||||
return "", "", fmt.Errorf("config file not found: %s", path)
|
return "", "", fmt.Errorf("config file not found: %s", path)
|
||||||
}
|
}
|
||||||
@@ -1002,7 +1004,7 @@ func resolveConfigPath(cliConfigPath string, cliConfigPathSet bool, lookup func(
|
|||||||
if path == "" {
|
if path == "" {
|
||||||
return "", "", fmt.Errorf("AUDITA_CONFIG must not be empty")
|
return "", "", fmt.Errorf("AUDITA_CONFIG must not be empty")
|
||||||
}
|
}
|
||||||
if _, statErr := os.Stat(path); statErr != nil {
|
if _, statErr := statConfigPath(path); statErr != nil {
|
||||||
if os.IsNotExist(statErr) {
|
if os.IsNotExist(statErr) {
|
||||||
return "", "", fmt.Errorf("config file not found: %s", path)
|
return "", "", fmt.Errorf("config file not found: %s", path)
|
||||||
}
|
}
|
||||||
@@ -1011,12 +1013,13 @@ func resolveConfigPath(cliConfigPath string, cliConfigPathSet bool, lookup func(
|
|||||||
return path, "env", nil
|
return path, "env", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultPath := config.DefaultConfigPath
|
for _, defaultPath := range config.DefaultConfigSearchPaths {
|
||||||
if _, statErr := os.Stat(defaultPath); statErr == nil {
|
if _, statErr := statConfigPath(defaultPath); statErr == nil {
|
||||||
return defaultPath, "default", nil
|
return defaultPath, "default", nil
|
||||||
} else if !os.IsNotExist(statErr) {
|
} else if !os.IsNotExist(statErr) {
|
||||||
return "", "", fmt.Errorf("cannot access config file %s: %w", defaultPath, statErr)
|
return "", "", fmt.Errorf("cannot access config file %s: %w", defaultPath, statErr)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return "", "", nil
|
return "", "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,55 @@ func TestResolveConfigPathDefaultIgnoredWhenMissing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResolveConfigPathDefaultPrefersUsrLocalOverEtc(t *testing.T) {
|
||||||
|
oldStat := statConfigPath
|
||||||
|
statConfigPath = func(path string) (os.FileInfo, error) {
|
||||||
|
if path == config.DefaultConfigPathUsrLocal || path == config.DefaultConfigPath {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, os.ErrNotExist
|
||||||
|
}
|
||||||
|
t.Cleanup(func() { statConfigPath = oldStat })
|
||||||
|
|
||||||
|
lookup := func(string) (string, bool) { return "", false }
|
||||||
|
path, source, err := resolveConfigPath("", false, lookup)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if source != "default" {
|
||||||
|
t.Fatalf("expected default source, got %q", source)
|
||||||
|
}
|
||||||
|
if path != config.DefaultConfigPathUsrLocal {
|
||||||
|
t.Fatalf("expected %q, got %q", config.DefaultConfigPathUsrLocal, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveConfigPathDefaultFallsBackToEtc(t *testing.T) {
|
||||||
|
oldStat := statConfigPath
|
||||||
|
statConfigPath = func(path string) (os.FileInfo, error) {
|
||||||
|
if path == config.DefaultConfigPathUsrLocal {
|
||||||
|
return nil, os.ErrNotExist
|
||||||
|
}
|
||||||
|
if path == config.DefaultConfigPath {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, os.ErrNotExist
|
||||||
|
}
|
||||||
|
t.Cleanup(func() { statConfigPath = oldStat })
|
||||||
|
|
||||||
|
lookup := func(string) (string, bool) { return "", false }
|
||||||
|
path, source, err := resolveConfigPath("", false, lookup)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if source != "default" {
|
||||||
|
t.Fatalf("expected default source, got %q", source)
|
||||||
|
}
|
||||||
|
if path != config.DefaultConfigPath {
|
||||||
|
t.Fatalf("expected %q, got %q", config.DefaultConfigPath, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRunConfigValidateSuccess(t *testing.T) {
|
func TestRunConfigValidateSuccess(t *testing.T) {
|
||||||
var stdout bytes.Buffer
|
var stdout bytes.Buffer
|
||||||
var stderr bytes.Buffer
|
var stderr bytes.Buffer
|
||||||
|
|||||||
@@ -6,7 +6,15 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DefaultConfigPath = "/etc/audita/config.yml"
|
const (
|
||||||
|
DefaultConfigPath = "/etc/audita/config.yml"
|
||||||
|
DefaultConfigPathUsrLocal = "/usr/local/etc/audita/config.yml"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DefaultConfigSearchPaths = []string{
|
||||||
|
DefaultConfigPathUsrLocal,
|
||||||
|
DefaultConfigPath,
|
||||||
|
}
|
||||||
|
|
||||||
func LoadFromEnv() (Config, error) {
|
func LoadFromEnv() (Config, error) {
|
||||||
cfg := Default()
|
cfg := Default()
|
||||||
|
|||||||
Reference in New Issue
Block a user