Require nonblank notification identities

This commit is contained in:
2026-08-13 00:21:20 +00:00
parent 706086e3de
commit 1d3ea64541
6 changed files with 40 additions and 4 deletions

View File

@@ -135,6 +135,9 @@ Distributor notification is disabled by default. Its fields are:
The upload token is read from the environment variable named by `token_env`.
Use `secrets.directory` when a file-backed secret is appropriate.
When notification is enabled, each rendered single-report pipeline ID, bundle
ID, and idempotency key must contain at least one non-whitespace character.
Single-report bundle templates accept `location_id`, `report_id`, `run_id`,
`artifact_group`, `batch_output_name`, `valid_start_date`, `valid_end_date`,
`valid_start_time`, `valid_end_time`, `valid_start_stamp`, `valid_end_stamp`,

View File

@@ -23,8 +23,8 @@ A successful response is `202 Accepted` with JSON containing `run_id` and
`status`. Acceptance means Distributor staged and validated the source bundle;
it does not mean downstream destinations have published it.
The adapter requires a pipeline ID, bundle ID, idempotency key, and at least one
source-file mapping before calling Distributor. It reads the bearer token from
The adapter requires nonblank pipeline ID, bundle ID, and idempotency key, plus
at least one source-file mapping, before calling Distributor. It reads the bearer token from
the configured environment variable and redacts that value from errors. Request
construction and timeout handling belong to the [Distributor adapter](../../internal/distributor-adapter.md).

View File

@@ -69,6 +69,8 @@ returns a failed batch status even when all report counters show success; the
top-level notification result contains the delivery diagnostic.
For a single report, Distributor notification follows the atomic output write.
Enabled notification templates are validated before report processing, including
the requirement that each rendered identity contains a non-whitespace character.
See the [configuration reference](config.md) for pipeline, bundle,
idempotency-key, and per-report path templates.

View File

@@ -1285,6 +1285,13 @@ func TestEnabledDistributorNotifyValidation(t *testing.T) {
},
wantErr: "notify.distributor.bundle_id_template",
},
{
name: "BundleTemplateRenderedEmpty",
mutate: func(cfg *Config) {
cfg.Notify.Distributor.BundleIDTemplate = " "
},
wantErr: "notify.distributor.bundle_id_template",
},
{
name: "IdempotencyTemplate",
mutate: func(cfg *Config) {
@@ -1292,6 +1299,13 @@ func TestEnabledDistributorNotifyValidation(t *testing.T) {
},
wantErr: "notify.distributor.idempotency_key_template",
},
{
name: "IdempotencyTemplateRenderedEmpty",
mutate: func(cfg *Config) {
cfg.Notify.Distributor.IdempotencyKeyTemplate = " "
},
wantErr: "notify.distributor.idempotency_key_template",
},
{
name: "BatchTemplate",
mutate: func(cfg *Config) {

View File

@@ -78,7 +78,14 @@ var distributorBatchIdempotencyTemplateVariables = map[string]struct{}{
var distributorBatchPipelineTemplateVariables = distributorBatchTemplateVariables
func RenderDistributorBundleID(template string, values DistributorTemplateValues) (string, error) {
return renderDistributorTemplate("notify.distributor.bundle_id_template", template, values, distributorTemplateVariables)
rendered, err := renderDistributorTemplate("notify.distributor.bundle_id_template", template, values, distributorTemplateVariables)
if err != nil {
return "", err
}
if strings.TrimSpace(rendered) == "" {
return "", fmt.Errorf("notify.distributor.bundle_id_template renders an empty bundle id")
}
return rendered, nil
}
func RenderDistributorPipelineID(template string, values DistributorTemplateValues) (string, error) {
@@ -93,7 +100,14 @@ func RenderDistributorPipelineID(template string, values DistributorTemplateValu
}
func RenderDistributorIdempotencyKey(template string, values DistributorTemplateValues) (string, error) {
return renderDistributorTemplate("notify.distributor.idempotency_key_template", template, values, distributorIdempotencyTemplateVariables)
rendered, err := renderDistributorTemplate("notify.distributor.idempotency_key_template", template, values, distributorIdempotencyTemplateVariables)
if err != nil {
return "", err
}
if strings.TrimSpace(rendered) == "" {
return "", fmt.Errorf("notify.distributor.idempotency_key_template renders an empty idempotency key")
}
return rendered, nil
}
func RenderDistributorBatchBundleID(template string, values DistributorBatchTemplateValues) (string, error) {

View File

@@ -152,6 +152,9 @@ func validateDistributorNotify(cfg DistributorNotifyConfig) error {
if _, err := RenderDistributorPipelineID(cfg.PipelineIDTemplate, values); err != nil {
return err
}
if _, err := RenderDistributorIdempotencyKey(cfg.IdempotencyKeyTemplate, values); err != nil {
return err
}
if err := validateDistributorBatchNotify(cfg.Batch); err != nil {
return err
}