Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
6 changes: 6 additions & 0 deletions .chloggen/group-stability-check.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
change_type: enhancement
component: docs
Comment thread
lmolkova marked this conversation as resolved.
note: >
Don't allow signal definition to reference attributes with lower stability
than the signal itself (unless they are opt-in).
issues: [3752]
14 changes: 9 additions & 5 deletions docs/general/semantic-convention-groups.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ to preserve code generation and documentation for legacy instrumentations.

When group is renamed or no longer recommended, it SHOULD be deprecated.

A group SHOULD NOT reference attributes with a lower maturity level than the
group itself, unless the requirement level of that attribute is `opt_in`.
The requirement level of such an attribute reference MAY be changed once the
attribute reaches the group's stability level, in cases allowed by
[Versioning and Stability][Stability].
For example, a `stable` event may reference a `development` attribute only at
the `opt_in` requirement level; that requirement level may change once the
attribute becomes stable as well.

See [Versioning and Stability][Stability] for the details on stability guarantees
provided for semantic convention groups of different types.

Expand All @@ -71,11 +80,6 @@ Stability guarantees on a group level **do not** apply to unstable attribute ref

- MAY add or remove references to unstable attributes with `opt_in`
requirement level.
- SHOULD NOT have references to unstable attributes with requirement level
other than `opt_in`.
The requirement level of an unstable attribute reference
MAY be changed when this attribute becomes stable in cases allowed by the
[Versioning and Stability][Stability].
- MUST NOT remove references to stable attributes.

Stable instrumentations MUST NOT report telemetry following the unstable part
Expand Down
28 changes: 23 additions & 5 deletions policies/group_stability.rego
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
package after_resolution
import rego.v1
# checks that stable group does not have experimental attributes with requirement levels other than opt_in

# Maturity ranking of stability levels. Higher number == more mature/stable.
stability_rank := {
"development": 1,
"experimental": 1,
"alpha": 2,
"beta": 3,
"release_candidate": 4,
"stable": 5,
}

# checks that a group's stability is not higher than the stability of any of its
# attributes, unless that attribute is opt_in
deny contains group_stability_violation(description, group.id, name) if {
group := input.groups[_]
# ignore attribute_groups
group.type != "attribute_group"
group.stability == "stable"

exceptions = {
# TODO: https://github.com/open-telemetry/semantic-conventions/issues/1514
Expand All @@ -14,17 +25,24 @@ deny contains group_stability_violation(description, group.id, name) if {
"entity.service",
# TODO: https://github.com/open-telemetry/semantic-conventions/issues/1616
"metric.dotnet.process.cpu.time",
# TODO: https://github.com/open-telemetry/semantic-conventions/issues/3753
"span.db.oracledb.client",
}
not exceptions[group.id]

attr := group.attributes[_]
group_rank := stability_rank[group.stability]

attr.stability != "stable"
attr := group.attributes[_]
attr.requirement_level != "opt_in"
Comment thread
lmolkova marked this conversation as resolved.
Outdated

attr_rank := stability_rank[attr.stability]

# group claims a higher maturity than the attribute it references
group_rank > attr_rank

name := attr.name

description := sprintf("Stable group '%s' references experimental attribute with requirement level '%s', only 'opt_in' level is allowed", [group.id, name])
description := sprintf("Group '%s' has stability '%s' which is higher than the stability '%s' of attribute '%s'; lower-stability attributes are only allowed at 'opt_in' requirement level", [group.id, group.stability, attr.stability, name])
}

group_stability_violation(description, group, attr) = violation if {
Expand Down
87 changes: 61 additions & 26 deletions policies_test/group_stability_test.rego
Original file line number Diff line number Diff line change
@@ -1,34 +1,69 @@
package after_resolution
import future.keywords

test_fails_on_experimental_not_opt_in_attribute_in_stable_group if {
experimental_stabilities := ["experimental", "development", "alpha", "beta", "release_candidate"]
every stability in experimental_stabilities {
count(deny) == 1 with input as {"groups": [{ "id": "span.foo",
"type": "span",
"stability": "stable",
"brief": "brief.",
"attributes": [{
"name": "test.experimental",
"stability": stability,
"requirement_level": "required",
"brief": "brief.",
}]}]}
gs_group_types := ["span", "metric", "event", "entity"]

gs_lower_stabilities := ["experimental", "development", "alpha", "beta", "release_candidate"]

# Builds an input with a single group of the given type referencing a single attribute.
gs_input(group_type, group_stability, attr_stability, requirement_level) := {"groups": [{
"id": sprintf("%s.foo", [group_type]),
"type": group_type,
"stability": group_stability,
"brief": "brief.",
"attributes": [{
"name": "test.attr",
"stability": attr_stability,
"requirement_level": requirement_level,
"brief": "brief.",
}],
}]}

# A group must not reference a lower-maturity attribute unless it's opt_in.
test_fails_on_lower_maturity_not_opt_in_attribute if {
every group_type in gs_group_types {
every stability in gs_lower_stabilities {
count(deny) == 1 with input as gs_input(group_type, "stable", stability, "required")
}
}
}

test_passes_on_lower_maturity_opt_in_attribute if {
Comment thread
lmolkova marked this conversation as resolved.
every group_type in gs_group_types {
every stability in gs_lower_stabilities {
count(deny) == 0 with input as gs_input(group_type, "stable", stability, "opt_in")
}
}
}

# Group stability higher than the attribute's (non-stable group) still fails.
test_fails_when_group_stability_higher_than_attribute if {
every group_type in gs_group_types {
count(deny) == 1 with input as gs_input(group_type, "release_candidate", "development", "required")
}
}

test_passes_on_experimental_opt_in_attribute_in_stable_group if {
experimental_stabilities := ["experimental", "development", "alpha", "beta", "release_candidate"]
every stability in experimental_stabilities {
count(deny) == 0 with input as {"groups": [{ "id": "span.foo",
"type": "span",
"stability": "stable",
"brief": "brief.",
"attributes": [{
"name": "test.experimental",
"stability": stability,
"requirement_level": "opt_in",
"brief": "brief.",
}]}]}
# A lower-stability group referencing a higher-stability attribute is fine.
test_passes_when_group_stability_lower_than_attribute if {
every group_type in gs_group_types {
count(deny) == 0 with input as gs_input(group_type, "development", "stable", "required")
}
}

test_passes_when_group_and_attribute_have_equal_stability if {
every group_type in gs_group_types {
count(deny) == 0 with input as gs_input(group_type, "release_candidate", "release_candidate", "required")
}
}

# A lower-stability attribute is allowed at opt_in in a more stable group.
test_passes_on_lower_stability_opt_in_attribute if {
every group_type in gs_group_types {
count(deny) == 0 with input as gs_input(group_type, "release_candidate", "development", "opt_in")
}
}

# attribute_group is exempt from the check regardless of stability.
test_passes_on_attribute_group if {
count(deny) == 0 with input as gs_input("attribute_group", "stable", "development", "required")
}
Loading