1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
# Build-time test for the configuration validation of the
# services.opentelemetry-collector module. It needs no VM: everything under
# test happens while the configuration is evaluated and built.
{
lib,
runCommand,
testers,
writeText,
evalSystem,
}:
let
evalService =
module:
(evalSystem {
services.opentelemetry-collector = {
enable = true;
}
// module;
}).config;
optionsOf = config: config.services.opentelemetry-collector;
execStartOf = config: config.systemd.services.opentelemetry-collector.serviceConfig.ExecStart;
confOf = config: config.system.build.opentelemetryCollectorConfig;
minimalSettings = {
receivers.otlp.protocols.http = { };
exporters.debug = { };
service.pipelines.logs = {
receivers = [ "otlp" ];
exporters = [ "debug" ];
};
};
secretRef = "\${file:/var/lib/secrets/otel-token}";
# A configuration that reads a secret through a confmap provider. Validation
# fails on it unless the property is overridden, because the file does not
# exist in the build sandbox.
secretSettings = {
receivers.otlp.protocols.http = { };
exporters.otlphttp = {
endpoint = "http://localhost:4318";
headers.authorization = "Bearer ${secretRef}";
};
service.pipelines.logs = {
receivers = [ "otlp" ];
exporters = [ "otlphttp" ];
};
};
# A pipeline that references an exporter nobody configured.
invalidSettings = {
receivers.otlp.protocols.http = { };
exporters.debug = { };
service.pipelines.logs = {
receivers = [ "otlp" ];
exporters = [ "nonexistent" ];
};
};
fromSettings = evalService { settings = minimalSettings; };
fromStoreConfigFile = evalService {
configFile = writeText "config.yaml" (builtins.toJSON minimalSettings);
};
fromExternalConfigFile = evalService {
configFile = "/etc/opentelemetry-collector/config.yaml";
};
withOverrides = evalService {
settings = secretSettings;
validateConfigOverrides = [ "exporters::otlphttp::headers::authorization=stub" ];
};
withoutOverrides = evalService { settings = secretSettings; };
invalid = evalService { settings = invalidSettings; };
in
# A configuration built from `settings` is always a store path, so it is always
# validated. This is the regression that motivated the option: the default used
# to be `isStorePath cfg.configFile`, which is false when `configFile` is null.
assert (optionsOf fromSettings).validateConfigFile;
assert (optionsOf fromStoreConfigFile).validateConfigFile;
# A configFile outside the store does not exist at build time, so validation
# must stay off and the service must point at the path verbatim.
assert !(optionsOf fromExternalConfigFile).validateConfigFile;
assert lib.hasSuffix "--config=file:/etc/opentelemetry-collector/config.yaml" (
execStartOf fromExternalConfigFile
);
runCommand "opentelemetry-collector-validate-test"
{
# Building `validated` runs `otelcol validate`, which only passes because
# the `--set` override replaces the file provider reference.
validated = confOf withOverrides;
invalidFailure = testers.testBuildFailure (confOf invalid);
unresolvableFailure = testers.testBuildFailure (confOf withoutOverrides);
}
''
# The override applies to validation only: the deployed configuration keeps
# the provider reference.
grep -F ${lib.escapeShellArg secretRef} $validated
! grep -F stub $validated
# Validation rejects a config whose pipeline references an undefined
# component.
grep -F nonexistent $invalidFailure/testBuildFailure.log
# Without an override, validation resolves the file provider and fails,
# because the secret does not exist in the sandbox.
grep -F /var/lib/secrets/otel-token $unresolvableFailure/testBuildFailure.log
touch $out
''
|