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
2 changes: 1 addition & 1 deletion cmd/cbox-init/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ func runServe(cmd *cobra.Command, args []string) {
Enabled: cfg.Global.TracingEnabled,
Exporter: cfg.Global.TracingExporter,
Endpoint: cfg.Global.TracingEndpoint,
SampleRate: cfg.Global.TracingSampleRate,
SampleRate: cfg.Global.TracingSampleRateValue(),
ServiceName: cfg.Global.TracingServiceName,
Version: version,
UseTLS: cfg.Global.TracingUseTLS,
Expand Down
2 changes: 1 addition & 1 deletion docs/configuration/global-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ global:
**Settings:**
- `api_enabled` - Enable/disable TCP REST API (default: `false`)
- `api_port` - HTTP port for API endpoints (default: `9180`)
- `api_host` - Bind host for the API listener (default: all interfaces). **Recommended:** set to `127.0.0.1` when you only need local access, so the management API is never reachable from the pod/host network.
- `api_host` - Bind host for the API listener (default: `127.0.0.1`, loopback only). Binding beyond loopback exposes a control plane that can start, stop and reconfigure every process in the container, so cbox-init refuses to start unless you also set `api_auth` or an `api_acl` with `mode: allow` and a non-empty `allow_list`.
- `api_auth` - Optional Bearer token for authentication

**API Endpoints:**
Expand Down
23 changes: 23 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ func (c *Config) validateGlobal() error {
if err := validateACL("metrics_acl", c.Global.MetricsACL); err != nil {
return err
}
if err := c.validateTracing(); err != nil {
return err
}
return nil
}

Expand All @@ -127,6 +130,26 @@ func validateACL(key string, acl *ACLConfig) error {
}
}

// validateTracing rejects an exporter the tracing provider cannot build.
//
// otlp-http, jaeger and zipkin were advertised in the field's own comment and
// given default endpoints, but createExporter implements only otlp-grpc and
// stdout — so check-config passed a config that made serve exit 1 at startup
// with "unsupported trace exporter". A config the validator calls valid must
// boot.
func (c *Config) validateTracing() error {
if !c.Global.TracingEnabled {
return nil
}

switch c.Global.TracingExporter {
case "", "otlp-grpc", "stdout":
return nil
default:
return fmt.Errorf("invalid tracing_exporter %q (valid: otlp-grpc, stdout)", c.Global.TracingExporter)
}
}

// validateAPIExposure refuses to start an unauthenticated management API on a
// non-loopback interface. The API can add and stop processes and rewrite the
// config, so exposing it beyond localhost without a bearer token or an IP ACL is
Expand Down
86 changes: 86 additions & 0 deletions internal/config/tracing_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package config

import (
"os"
"path/filepath"
"testing"
)

// TestTracingSampleRateZeroSamplesNothing: SetDefaults treated 0 as "unset" and
// replaced it with 1.0, so tracing_sample_rate: 0.0 — the documented way to
// sample nothing — produced 100% sampling instead. The docs even advise
// "ensure not 0.0", which only makes sense if 0.0 means what it says.
func TestTracingSampleRateZeroSamplesNothing(t *testing.T) {
tests := []struct {
name string
yaml string
want float64
}{
{"explicit zero", " tracing_sample_rate: 0.0\n", 0.0},
{"explicit fraction", " tracing_sample_rate: 0.25\n", 0.25},
{"absent defaults to full", "", 1.0},
{"explicit one", " tracing_sample_rate: 1.0\n", 1.0},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := loadTracingConfig(t, " tracing_enabled: true\n tracing_exporter: stdout\n"+tt.yaml)

if got := cfg.Global.TracingSampleRateValue(); got != tt.want {
t.Errorf("sample rate = %v, want %v", got, tt.want)
}
})
}
}

// TestUnsupportedTracingExporterIsRejected: otlp-http, jaeger and zipkin were
// advertised in the field's comment and given default endpoints, but the
// provider implements only otlp-grpc and stdout. check-config passed, and then
// serve exited 1 at startup. A config the validator calls valid must boot.
func TestUnsupportedTracingExporterIsRejected(t *testing.T) {
for _, exporter := range []string{"otlp-http", "jaeger", "zipkin", "otlp"} {
t.Run(exporter, func(t *testing.T) {
path := writeTracingConfig(t, " tracing_enabled: true\n tracing_exporter: "+exporter+"\n")

if _, err := LoadWithEnvExpansion(path); err == nil {
t.Errorf("tracing_exporter %q validated, but the provider cannot build it "+
"— serve would exit 1 at startup", exporter)
}
})
}

for _, exporter := range []string{"otlp-grpc", "stdout"} {
t.Run(exporter, func(t *testing.T) {
path := writeTracingConfig(t, " tracing_enabled: true\n tracing_exporter: "+exporter+"\n")

if _, err := LoadWithEnvExpansion(path); err != nil {
t.Errorf("tracing_exporter %q rejected: %v", exporter, err)
}
})
}
}

func writeTracingConfig(t *testing.T, globalExtra string) string {
t.Helper()

body := "version: \"1.0\"\nglobal:\n log_level: info\n" + globalExtra +
"processes:\n app:\n command: [\"/bin/true\"]\n"

path := filepath.Join(t.TempDir(), "cbox-init.yaml")
if err := os.WriteFile(path, []byte(body), 0o600); err != nil {
t.Fatal(err)
}

return path
}

func loadTracingConfig(t *testing.T, globalExtra string) *Config {
t.Helper()

cfg, err := LoadWithEnvExpansion(writeTracingConfig(t, globalExtra))
if err != nil {
t.Fatalf("load: %v", err)
}

return cfg
}
Loading
Loading