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`. The upload token is read from the environment variable named by `token_env`.
Use `secrets.directory` when a file-backed secret is appropriate. 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`, Single-report bundle templates accept `location_id`, `report_id`, `run_id`,
`artifact_group`, `batch_output_name`, `valid_start_date`, `valid_end_date`, `artifact_group`, `batch_output_name`, `valid_start_date`, `valid_end_date`,
`valid_start_time`, `valid_end_time`, `valid_start_stamp`, `valid_end_stamp`, `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; `status`. Acceptance means Distributor staged and validated the source bundle;
it does not mean downstream destinations have published it. it does not mean downstream destinations have published it.
The adapter requires a pipeline ID, bundle ID, idempotency key, and at least one The adapter requires nonblank pipeline ID, bundle ID, and idempotency key, plus
source-file mapping before calling Distributor. It reads the bearer token from 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 the configured environment variable and redacts that value from errors. Request
construction and timeout handling belong to the [Distributor adapter](../../internal/distributor-adapter.md). 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. top-level notification result contains the delivery diagnostic.
For a single report, Distributor notification follows the atomic output write. 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, See the [configuration reference](config.md) for pipeline, bundle,
idempotency-key, and per-report path templates. idempotency-key, and per-report path templates.

View File

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

View File

@@ -78,7 +78,14 @@ var distributorBatchIdempotencyTemplateVariables = map[string]struct{}{
var distributorBatchPipelineTemplateVariables = distributorBatchTemplateVariables var distributorBatchPipelineTemplateVariables = distributorBatchTemplateVariables
func RenderDistributorBundleID(template string, values DistributorTemplateValues) (string, error) { 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) { 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) { 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) { 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 { if _, err := RenderDistributorPipelineID(cfg.PipelineIDTemplate, values); err != nil {
return err return err
} }
if _, err := RenderDistributorIdempotencyKey(cfg.IdempotencyKeyTemplate, values); err != nil {
return err
}
if err := validateDistributorBatchNotify(cfg.Batch); err != nil { if err := validateDistributorBatchNotify(cfg.Batch); err != nil {
return err return err
} }