(chore) Move backwards compatibility checks to weaver. (#1327)
Co-authored-by: Liudmila Molkova <limolkova@microsoft.com>
This commit is contained in:
parent
881b3f0b80
commit
bad36854b2
|
|
@ -86,13 +86,6 @@ jobs:
|
|||
make attribute-registry-generation
|
||||
git diff --exit-code './docs/attributes-registry/*.md' || (echo 'Attribute registry markdown is out of date, please run "make attribute-registry-generation" and commit the changes in this PR.' && exit 1)
|
||||
|
||||
semantic-conventions-compatibility:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: verify semantic convention compatibility with latest released version
|
||||
run: make compatibility-check
|
||||
|
||||
schemas-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
|
@ -115,3 +108,18 @@ jobs:
|
|||
- uses: actions/checkout@v1
|
||||
- name: verify semantic conventions yaml definitions
|
||||
run: make check-policies
|
||||
|
||||
polices-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: verify semantic conventions yaml definitions
|
||||
run: make test-policies
|
||||
|
||||
# TODO: Remove this once policies-check is the only enforcement on github.
|
||||
semantic-conventions-compatibility:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: verify semantic convention compatibility with latest released version
|
||||
run: make compatibility-check
|
||||
|
|
|
|||
75
Makefile
75
Makefile
|
|
@ -22,7 +22,11 @@ CHLOGGEN_CONFIG := .chloggen/config.yaml
|
|||
# see https://github.com/open-telemetry/build-tools/releases for semconvgen updates
|
||||
# Keep links in model/README.md and .vscode/settings.json in sync!
|
||||
SEMCONVGEN_VERSION=0.25.0
|
||||
WEAVER_VERSION=0.7.0
|
||||
|
||||
# see https://github.com/open-telemetry/weaver/releases for weaver updates
|
||||
WEAVER_VERSION=0.8.0
|
||||
|
||||
OPA_POLICY_AGENT_VERSION=0.67.1
|
||||
|
||||
# From where to resolve the containers (e.g. "otel/weaver").
|
||||
CONTAINER_REPOSITORY=docker.io
|
||||
|
|
@ -111,20 +115,6 @@ install-yamllint:
|
|||
yamllint:
|
||||
yamllint .
|
||||
|
||||
# Check semantic convention policies on YAML files
|
||||
.PHONY: check-policies
|
||||
check-policies:
|
||||
docker run --rm -v $(PWD)/model:/source -v $(PWD)/policies:/policies -v $(PWD)/templates:/templates \
|
||||
otel/weaver:${WEAVER_VERSION} registry check \
|
||||
--registry=/source \
|
||||
--diagnostic-format=ansi \
|
||||
--policy=/policies/registry.rego
|
||||
|
||||
# Test rego policies
|
||||
.PHONY: test-policies
|
||||
test-policies:
|
||||
docker run --rm -v $(PWD)/policies:/policies openpolicyagent/opa:0.67.1 test --explain fails /policies
|
||||
|
||||
# Generate markdown tables from YAML definitions
|
||||
.PHONY: table-generation
|
||||
table-generation:
|
||||
|
|
@ -159,26 +149,6 @@ table-check:
|
|||
--dry-run \
|
||||
/spec
|
||||
|
||||
|
||||
# A previous iteration of calculating "LATEST_RELEASED_SEMCONV_VERSION"
|
||||
# relied on "git describe". However, that approach does not work with
|
||||
# light-weight developer forks/branches that haven't synced tags. Hence the
|
||||
# more complex implementation of this using "git ls-remote".
|
||||
#
|
||||
# The output of "git ls-remote" looks something like this:
|
||||
#
|
||||
# e531541025992b68177a68b87628c5dc75c4f7d9 refs/tags/v1.21.0
|
||||
# cadfe53949266d33476b15ca52c92f682600a29c refs/tags/v1.22.0
|
||||
# ...
|
||||
#
|
||||
# .. which is why some additional processing is required to extract the
|
||||
# latest version number and strip off the "v" prefix.
|
||||
LATEST_RELEASED_SEMCONV_VERSION := $(shell git ls-remote --tags https://github.com/open-telemetry/semantic-conventions.git | cut -f 2 | sort --reverse | head -n 1 | tr '/' ' ' | cut -d ' ' -f 3 | $(SED) 's/v//g')
|
||||
.PHONY: compatibility-check
|
||||
compatibility-check:
|
||||
docker run --rm -v $(PWD)/model:/source -v $(PWD)/docs:/spec --pull=always \
|
||||
$(SEMCONVGEN_CONTAINER) -f /source compatibility --previous-version $(LATEST_RELEASED_SEMCONV_VERSION)
|
||||
|
||||
.PHONY: schema-check
|
||||
schema-check:
|
||||
$(TOOLS_DIR)/schema_check.sh
|
||||
|
|
@ -234,11 +204,40 @@ chlog-update: $(CHLOGGEN)
|
|||
generate-gh-issue-templates:
|
||||
$(TOOLS_DIR)/scripts/update-issue-template-areas.sh
|
||||
|
||||
# A previous iteration of calculating "LATEST_RELEASED_SEMCONV_VERSION"
|
||||
# relied on "git describe". However, that approach does not work with
|
||||
# light-weight developer forks/branches that haven't synced tags. Hence the
|
||||
# more complex implementation of this using "git ls-remote".
|
||||
#
|
||||
# The output of "git ls-remote" looks something like this:
|
||||
#
|
||||
# e531541025992b68177a68b87628c5dc75c4f7d9 refs/tags/v1.21.0
|
||||
# cadfe53949266d33476b15ca52c92f682600a29c refs/tags/v1.22.0
|
||||
# ...
|
||||
#
|
||||
# .. which is why some additional processing is required to extract the
|
||||
# latest version number and strip off the "v" prefix.
|
||||
LATEST_RELEASED_SEMCONV_VERSION := $(shell git ls-remote --tags https://github.com/open-telemetry/semantic-conventions.git | cut -f 2 | sort --reverse | head -n 1 | tr '/' ' ' | cut -d ' ' -f 3 | $(SED) 's/v//g')
|
||||
.PHONY: check-policies
|
||||
check-policies:
|
||||
docker run --rm -v $(PWD)/model:/source -v $(PWD)/docs:/spec -v $(PWD)/policies:/policies \
|
||||
otel/weaver:${WEAVER_VERSION} registry check \
|
||||
--registry=/source \
|
||||
--policy=/policies/registry.rego \
|
||||
--policy=/policies/attribute_name_collisions.rego \
|
||||
--policy=/policies/yaml_schema.rego
|
||||
--baseline-registry=https://github.com/open-telemetry/semantic-conventions/archive/refs/tags/v$(LATEST_RELEASED_SEMCONV_VERSION).zip[model] \
|
||||
--policy=/policies
|
||||
|
||||
# Test rego policies
|
||||
.PHONY: test-policies
|
||||
test-policies:
|
||||
docker run --rm -v $(PWD)/policies:/policies -v $(PWD)/policies_test:/policies_test \
|
||||
openpolicyagent/opa:${OPA_POLICY_AGENT_VERSION} test \
|
||||
--explain fails \
|
||||
/policies \
|
||||
/policies_test
|
||||
|
||||
# TODO: This is now duplicative with weaver policy checks. We can remove
|
||||
# once github action requirements are updated.
|
||||
.PHONY: compatibility-check
|
||||
compatibility-check:
|
||||
docker run --rm -v $(PWD)/model:/source -v $(PWD)/docs:/spec --pull=always \
|
||||
$(SEMCONVGEN_CONTAINER) -f /source compatibility --previous-version $(LATEST_RELEASED_SEMCONV_VERSION)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
package after_resolution
|
||||
|
||||
import rego.v1
|
||||
|
||||
# Data structures to make checking things faster.
|
||||
attribute_names := { data |
|
||||
group := input.groups[_]
|
||||
attr := group.attributes[_]
|
||||
data := { "name": attr.name, "const_name": to_const_name(attr.name), "namespace_prefix": to_namespace_prefix(attr.name) }
|
||||
attribute_names := { obj |
|
||||
group := input.groups[_];
|
||||
attr := group.attributes[_];
|
||||
obj := { "name": attr.name, "const_name": to_const_name(attr.name), "namespace_prefix": to_namespace_prefix(attr.name) }
|
||||
}
|
||||
|
||||
|
||||
deny[attr_registry_collision(description, name)] {
|
||||
deny contains attr_registry_collision(description, name) if {
|
||||
some i
|
||||
name := attribute_names[i].name
|
||||
const_name := attribute_names[i].const_name
|
||||
|
|
@ -24,7 +26,7 @@ deny[attr_registry_collision(description, name)] {
|
|||
description := sprintf("Attribute '%s' has the same constant name '%s' as '%s'.", [name, const_name, collisions])
|
||||
}
|
||||
|
||||
deny[attr_registry_collision(description, name)] {
|
||||
deny contains attr_registry_collision(description, name) if {
|
||||
some i
|
||||
name := attribute_names[i].name
|
||||
prefix := attribute_names[i].namespace_prefix
|
||||
|
|
@ -39,7 +41,7 @@ deny[attr_registry_collision(description, name)] {
|
|||
description := sprintf("Attribute '%s' is used as a namespace in '%s'.", [name, collisions])
|
||||
}
|
||||
|
||||
attr_registry_collision(description, attr_name) = violation {
|
||||
attr_registry_collision(description, attr_name) = violation if {
|
||||
violation := {
|
||||
"id": description,
|
||||
"type": "semconv_attribute",
|
||||
|
|
@ -49,11 +51,11 @@ attr_registry_collision(description, attr_name) = violation {
|
|||
}
|
||||
}
|
||||
|
||||
to_namespace_prefix(name) = namespace {
|
||||
to_namespace_prefix(name) = namespace if {
|
||||
namespace := concat("", [name, "."])
|
||||
}
|
||||
|
||||
to_const_name(name) = const_name {
|
||||
to_const_name(name) = const_name if {
|
||||
const_name := replace(name, ".", "_")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,352 @@
|
|||
package comparison_after_resolution
|
||||
|
||||
import rego.v1
|
||||
|
||||
# Semantic Convention Registry Compatibility Checker
|
||||
#
|
||||
# This file contains rules for checking backward compatibility
|
||||
# between different versions of semantic convention registries.
|
||||
# It builds upon the data structures and rules defined in the
|
||||
# semconv package.
|
||||
|
||||
# Import the previous release (baseline) and current sets of attributes, metrics.
|
||||
baseline_attributes := [attr |
|
||||
some g in data.semconv.registry_baseline_groups
|
||||
some attr in g.attributes
|
||||
]
|
||||
registry_attributes := [attr |
|
||||
some g in data.semconv.registry_groups
|
||||
some attr in g.attributes
|
||||
]
|
||||
registry_attribute_names := {attr.name |
|
||||
some g in data.semconv.registry_groups
|
||||
some attr in g.attributes
|
||||
}
|
||||
baseline_metrics := [ g |
|
||||
some g in data.semconv.baseline_groups
|
||||
g.type == "metric"
|
||||
]
|
||||
registry_metrics := [ g |
|
||||
some g in data.semconv.groups
|
||||
g.type == "metric"
|
||||
]
|
||||
registry_metric_names := { g.metric_name | some g in registry_metrics }
|
||||
|
||||
|
||||
# Rules we enforce:
|
||||
# - Attributes
|
||||
# - [x] Attributes cannot be removed
|
||||
# - [x] Attributes cannot "degrade" stability (stable->experimental)
|
||||
# - [x] Stable attributes cannot change type
|
||||
# - Enum members
|
||||
# - [x] Stable members cannot change stability
|
||||
# - [x] Values cannot change
|
||||
# - [x] ids cannot be removed
|
||||
# - Metrics
|
||||
# - [x] metrics cannot be removed
|
||||
# - [x] Stable metrics cannot become unstable
|
||||
# - [x] Stable Metric units cannot change
|
||||
# - [x] Stable Metric instruments cannot change
|
||||
# - [x] Set of required/recommended attributes must remain the same
|
||||
|
||||
|
||||
# Rule: Detect Removed Attributes
|
||||
#
|
||||
# This rule checks for attributes that existed in the baseline registry
|
||||
# but are no longer present in the current registry. Removing attributes
|
||||
# is considered a backward compatibility violation.
|
||||
#
|
||||
# In other words, we do not allow the removal of an attribute once added
|
||||
# to the registry. It must exist SOMEWHERE in a group, but may be deprecated.
|
||||
deny contains back_comp_violation(description, group_id, attr.name) if {
|
||||
# Check if an attribute from the baseline is missing in the current registry
|
||||
some attr in baseline_attributes
|
||||
not registry_attribute_names[attr.name]
|
||||
|
||||
# Generate human readable error.
|
||||
group_id := data.semconv.baseline_group_ids_by_attribute[attr.name]
|
||||
description := sprintf("Attribute '%s' no longer exists in the attribute registry", [attr.name])
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Rule: Detect Stable Attributes moving to unstable
|
||||
#
|
||||
# This rule checks for attributes that were stable in the baseline registry
|
||||
# but are no longer stable in the current registry. Once stable, attributes
|
||||
# remain forever but may be deprecated.
|
||||
deny contains back_comp_violation(description, group_id, attr.name) if {
|
||||
# Find stable baseline attributes in latest registry.
|
||||
some attr in baseline_attributes
|
||||
attr.stability == "stable"
|
||||
some nattr in registry_attributes
|
||||
attr.name == nattr.name
|
||||
|
||||
# Enforce the policy
|
||||
attr.stability != nattr.stability
|
||||
|
||||
# Generate human readable error.
|
||||
group_id := data.semconv.baseline_group_ids_by_attribute[attr.name]
|
||||
description := sprintf("Attribute '%s' was stable, but has new stability marker", [attr.name])
|
||||
}
|
||||
|
||||
# Rule: Detect Stable Attributes changing type
|
||||
#
|
||||
# This rule checks for attributes that were stable in the baseline registry
|
||||
# but are no longer stable in the current registry. Once stable, attributes
|
||||
# remain forever but may be deprecated.
|
||||
deny contains back_comp_violation(description, group_id, attr.name) if {
|
||||
# Find stable baseline attributes in latest registry.
|
||||
some attr in baseline_attributes
|
||||
attr.stability == "stable"
|
||||
some nattr in registry_attributes
|
||||
attr.name == nattr.name
|
||||
|
||||
# Enforce the policy
|
||||
# TODO - deal with enum type changes, probably in enum sections
|
||||
not is_enum(attr)
|
||||
attr.type != nattr.type
|
||||
|
||||
# Generate human readable error.
|
||||
group_id := data.semconv.baseline_group_ids_by_attribute[attr.name]
|
||||
attr_type_string := type_string(attr)
|
||||
nattr_type_string := type_string(nattr)
|
||||
description := sprintf("Attribute '%s' was '%s', but has new type '%s'", [attr.name, attr_type_string, nattr_type_string])
|
||||
}
|
||||
|
||||
# Rule: Detect Stable enum Attributes changing type
|
||||
#
|
||||
# This rule checks for attributes that were stable in the baseline registry
|
||||
# but are no longer stable in the current registry. Once stable, attributes
|
||||
# remain forever but may be deprecated.
|
||||
deny contains back_comp_violation(description, group_id, attr.name) if {
|
||||
# Find stable baseline attributes in latest registry.
|
||||
some attr in baseline_attributes
|
||||
attr.stability == "stable"
|
||||
some nattr in registry_attributes
|
||||
attr.name == nattr.name
|
||||
# Enforce the policy
|
||||
attr.type != nattr.type
|
||||
is_enum(attr)
|
||||
not is_enum(nattr)
|
||||
|
||||
# Generate human readable error.
|
||||
group_id := data.semconv.baseline_group_ids_by_attribute[attr.name]
|
||||
nattr_type_string := type_string(nattr)
|
||||
description := sprintf("Attribute '%s' was enum, but has new type '%s'", [attr.name, nattr_type_string])
|
||||
}
|
||||
|
||||
# Rule: Detect Stable Enum members changing stability level
|
||||
#
|
||||
# This rule checks for enum values that were stable in the baseline registry
|
||||
# but are no longer stable in the current registry.
|
||||
deny contains back_comp_violation(description, group_id, attr.name) if {
|
||||
# Find data we need to enforce: Enums in baseline/current.
|
||||
some attr in baseline_attributes
|
||||
attr.stability == "stable"
|
||||
some nattr in registry_attributes
|
||||
attr.name == nattr.name
|
||||
is_enum(attr)
|
||||
some member in attr.type.members
|
||||
some nmember in nattr.type.members
|
||||
member.id == nmember.id
|
||||
|
||||
# Enforce the policy
|
||||
member.stability == "stable"
|
||||
nmember.stability != "stable"
|
||||
|
||||
# Generate human readable error.
|
||||
group_id := data.semconv.baseline_group_ids_by_attribute[attr.name]
|
||||
description := sprintf("Enum '%s' had stable member '%s', but is no longer stable", [attr.name, member.id])
|
||||
}
|
||||
|
||||
# Rule: Enum member values cannot change
|
||||
#
|
||||
# This rule checks for enum values that have the same id, but values
|
||||
# are different.
|
||||
deny contains back_comp_violation(description, group_id, attr.name) if {
|
||||
# Find data we need to enforce: Enums in baseline/current.
|
||||
some attr in baseline_attributes
|
||||
attr.stability == "stable"
|
||||
some nattr in registry_attributes
|
||||
attr.name == nattr.name
|
||||
is_enum(attr)
|
||||
some member in attr.type.members
|
||||
some nmember in nattr.type.members
|
||||
member.id == nmember.id
|
||||
|
||||
# Enforce the policy
|
||||
member.value != nmember.value
|
||||
|
||||
# Generate human readable error.
|
||||
group_id := data.semconv.baseline_group_ids_by_attribute[attr.name]
|
||||
description := sprintf("Enum '%s' had stable value '%s', but is now '%s'", [attr.name, member.value, nmember.value])
|
||||
}
|
||||
|
||||
# Rule: Detect Stable Enum members missing
|
||||
#
|
||||
# This rule checks for enum values that were stable in the baseline registry
|
||||
# but are no longer have the same values in the current registry. Once stable,
|
||||
# enum values remain forever but may be deprecated.
|
||||
deny contains back_comp_violation(description, group_id, attr.name) if {
|
||||
# Find data we need to enforce: Enums in baseline/current.
|
||||
some attr in baseline_attributes
|
||||
attr.stability == "stable"
|
||||
some nattr in registry_attributes
|
||||
attr.name == nattr.name
|
||||
is_enum(attr)
|
||||
current_member_ids := {member.id | some member in nattr.type.members}
|
||||
# Enforce the policy
|
||||
some member in attr.type.members
|
||||
not current_member_ids[member.id]
|
||||
|
||||
# Generate human readable error.
|
||||
group_id := data.semconv.baseline_group_ids_by_attribute[attr.name]
|
||||
description := sprintf("Enum '%s' had member '%s', but is no longer defined", [attr.name, member.id])
|
||||
}
|
||||
|
||||
# Rule: Detect Removed Metrics
|
||||
#
|
||||
# This rule checks for stable metrics that existed in the baseline registry
|
||||
# but are no longer present in the current registry. Removing attributes
|
||||
# is considered a backward compatibility violation.
|
||||
#
|
||||
# In other words, we do not allow the removal of an attribute once added
|
||||
# to the registry. It must exist SOMEWHERE in a group, but may be deprecated.
|
||||
deny contains back_comp_violation(description, group_id, "") if {
|
||||
# Find data we need to enforce
|
||||
some metric in baseline_metrics
|
||||
metric.stability == "stable"
|
||||
# Enforce the policy
|
||||
not registry_metric_names[metric.metric_name]
|
||||
|
||||
# Generate human readable error.
|
||||
group_id := metric.id
|
||||
description := sprintf("Metric '%s' no longer exists in semantic conventions", [metric.metric_name])
|
||||
}
|
||||
|
||||
# Rule: Stable metrics cannot become unstable
|
||||
#
|
||||
# This rule checks that stable metrics cannot have their stability level changed.
|
||||
deny contains back_comp_violation(description, group_id, "") if {
|
||||
# Find data we need to enforce
|
||||
some metric in baseline_metrics
|
||||
metric.stability == "stable"
|
||||
some nmetric in registry_metrics
|
||||
metric.metric_name = nmetric.metric_name
|
||||
# Enforce the policy
|
||||
nmetric.stability != "stable"
|
||||
|
||||
# Generate human readable error.
|
||||
group_id := metric.id
|
||||
description := sprintf("Metric '%s' cannot change from stable", [metric.metric_name])
|
||||
}
|
||||
|
||||
# Rule: Stable metrics units cannot change
|
||||
#
|
||||
# This rule checks that stable metrics cannot change the unit type.
|
||||
deny contains back_comp_violation(description, group_id, "") if {
|
||||
# Find data we need to enforce
|
||||
some metric in baseline_metrics
|
||||
metric.stability == "stable"
|
||||
some nmetric in registry_metrics
|
||||
metric.metric_name = nmetric.metric_name
|
||||
# Enforce the policy
|
||||
nmetric.unit != metric.unit
|
||||
|
||||
# Generate human readable error.
|
||||
group_id := metric.id
|
||||
description := sprintf("Metric '%s' cannot change unit (was '%s', now: '%s')", [metric.metric_name, metric.unit, nmetric.unit])
|
||||
}
|
||||
|
||||
# Rule: Stable Metric instruments cannot change
|
||||
#
|
||||
# This rule checks that stable metrics cannot change the instrument type.
|
||||
deny contains back_comp_violation(description, group_id, "") if {
|
||||
# Find data we need to enforce
|
||||
some metric in baseline_metrics
|
||||
metric.stability == "stable"
|
||||
some nmetric in registry_metrics
|
||||
metric.metric_name = nmetric.metric_name
|
||||
# Enforce the policy
|
||||
nmetric.instrument != metric.instrument
|
||||
|
||||
# Generate human readable error.
|
||||
group_id := metric.id
|
||||
description := sprintf("Metric '%s' cannot change instrument (was '%s', now: '%s')", [metric.metric_name, metric.instrument, nmetric.instrument])
|
||||
}
|
||||
|
||||
# Rule: Stable Metric required/recommended attributes cannot change - missing
|
||||
#
|
||||
# This rule checks that stable metrics have stable sets of attributes.
|
||||
deny contains back_comp_violation(description, group_id, "") if {
|
||||
# Find data we need to enforce
|
||||
some metric in baseline_metrics
|
||||
metric.stability == "stable"
|
||||
some nmetric in registry_metrics
|
||||
metric.metric_name = nmetric.metric_name
|
||||
|
||||
baseline_attributes := { attr.name |
|
||||
some attr in metric.attributes
|
||||
not is_opt_in(attr)
|
||||
}
|
||||
new_attributes := { attr.name |
|
||||
some attr in nmetric.attributes
|
||||
not is_opt_in(attr)
|
||||
}
|
||||
missing_attributes := baseline_attributes - new_attributes
|
||||
# Enforce the policy
|
||||
count(missing_attributes) > 0
|
||||
|
||||
# Generate human readable error.
|
||||
group_id := metric.id
|
||||
description := sprintf("Metric '%s' cannot change required/recommended attributes (missing '%s')", [metric.metric_name, missing_attributes])
|
||||
}
|
||||
|
||||
# Rule: Stable Metric required/recommended attributes cannot change - added
|
||||
#
|
||||
# This rule checks that stable metrics have stable sets of attributes.
|
||||
deny contains back_comp_violation(description, group_id, "") if {
|
||||
# Find data we need to enforce
|
||||
some metric in baseline_metrics
|
||||
metric.stability == "stable"
|
||||
some nmetric in registry_metrics
|
||||
metric.metric_name = nmetric.metric_name
|
||||
|
||||
baseline_attributes := { attr.name |
|
||||
some attr in metric.attributes
|
||||
not is_opt_in(attr)
|
||||
}
|
||||
new_attributes := { attr.name |
|
||||
some attr in nmetric.attributes
|
||||
not is_opt_in(attr)
|
||||
}
|
||||
added_attributes := new_attributes - baseline_attributes
|
||||
# Enforce the policy
|
||||
count(added_attributes) > 0
|
||||
|
||||
# Generate human readable error.
|
||||
group_id := metric.id
|
||||
description := sprintf("Metric '%s' cannot change required/recommended attributes (added '%s')", [metric.metric_name, added_attributes])
|
||||
}
|
||||
|
||||
|
||||
# Helper Function: Create Backward Compatibility Violation Object
|
||||
#
|
||||
# This function generates a structured violation object for each
|
||||
# detected backward compatibility issue.
|
||||
back_comp_violation(description, group_id, attr_id) := violation if {
|
||||
violation := {
|
||||
"id": description,
|
||||
"type": "semconv_attribute",
|
||||
"category": "backward_compatibility",
|
||||
"group": group_id,
|
||||
"attr": attr_id,
|
||||
}
|
||||
}
|
||||
|
||||
# Helpers for enum values and type strings
|
||||
is_enum(attr) := true if count(attr.type.members) > 0
|
||||
type_string(attr) := attr.type if not is_enum(attr)
|
||||
type_string(attr) := "enum" if is_enum(attr)
|
||||
is_opt_in(attr) := true if attr.requirement_level == "opt_in"
|
||||
|
|
@ -0,0 +1,654 @@
|
|||
package comparison_after_resolution
|
||||
|
||||
import future.keywords.if
|
||||
|
||||
# Check that attributes cannot be removed.
|
||||
test_removed_attributes if {
|
||||
count(deny) > 0 with data.semconv as {
|
||||
"registry_baseline_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing"
|
||||
}]
|
||||
}],
|
||||
"baseline_group_ids_by_attribute": {
|
||||
"test.missing": "registry.test"
|
||||
}
|
||||
}
|
||||
count(deny) == 0 with data.semconv as {
|
||||
"registry_baseline_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing"
|
||||
}]
|
||||
}],
|
||||
"registry_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing"
|
||||
}]
|
||||
}],
|
||||
"baseline_group_ids_by_attribute": {
|
||||
"test.missing": "registry.test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Check that attributes cannot change stability
|
||||
test_attribute_stability_change if {
|
||||
count(deny) > 0 with data.semconv as {
|
||||
"registry_baseline_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
}]
|
||||
}],
|
||||
"registry_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "experimental",
|
||||
}]
|
||||
}],
|
||||
"baseline_group_ids_by_attribute": {
|
||||
"test.missing": "registry.test"
|
||||
}
|
||||
}
|
||||
count(deny) == 0 with data.semconv as {
|
||||
"registry_baseline_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
}]
|
||||
}],
|
||||
"registry_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
}]
|
||||
}],
|
||||
"baseline_group_ids_by_attribute": {
|
||||
"test.missing": "registry.test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Check stable attribute changing type
|
||||
test_attribute_type_change if {
|
||||
count(deny) > 0 with data.semconv as {
|
||||
"registry_baseline_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": "int",
|
||||
}]
|
||||
}],
|
||||
"registry_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": "string",
|
||||
}]
|
||||
}],
|
||||
"baseline_group_ids_by_attribute": {
|
||||
"test.missing": "registry.test"
|
||||
}
|
||||
}
|
||||
count(deny) == 0 with data.semconv as {
|
||||
"registry_baseline_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": "string",
|
||||
}]
|
||||
}],
|
||||
"registry_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": "string",
|
||||
}]
|
||||
}],
|
||||
"baseline_group_ids_by_attribute": {
|
||||
"test.missing": "registry.test"
|
||||
}
|
||||
}
|
||||
}
|
||||
# Check stable attribute enum type
|
||||
test_attribute_enum_type_change if {
|
||||
count(deny) > 0 with data.semconv as {
|
||||
"registry_baseline_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": {
|
||||
"members": [{
|
||||
"id": "test",
|
||||
"value": "test",
|
||||
}]
|
||||
},
|
||||
}]
|
||||
}],
|
||||
"registry_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": "string",
|
||||
}]
|
||||
}],
|
||||
"baseline_group_ids_by_attribute": {
|
||||
"test.missing": "registry.test"
|
||||
}
|
||||
}
|
||||
count(deny) == 0 with data.semconv as {
|
||||
"registry_baseline_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": {
|
||||
"members": [{
|
||||
"id": "test",
|
||||
"value": "test",
|
||||
}]
|
||||
},
|
||||
}]
|
||||
}],
|
||||
"registry_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": {
|
||||
"members": [{
|
||||
"id": "test",
|
||||
"value": "test",
|
||||
}]
|
||||
},
|
||||
}]
|
||||
}],
|
||||
"baseline_group_ids_by_attribute": {
|
||||
"test.missing": "registry.test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Check stable attribute enum members changing to nonstable
|
||||
test_attribute_enum_member_stability_change if {
|
||||
count(deny) > 0 with data.semconv as {
|
||||
"registry_baseline_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": {
|
||||
"members": [{
|
||||
"id": "test",
|
||||
"value": "test",
|
||||
"stability": "stable",
|
||||
}]
|
||||
},
|
||||
}]
|
||||
}],
|
||||
"registry_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": {
|
||||
"members": [{
|
||||
"id": "test",
|
||||
"value": "test",
|
||||
"stability": "experimental",
|
||||
}]
|
||||
},
|
||||
}]
|
||||
}],
|
||||
"baseline_group_ids_by_attribute": {
|
||||
"test.missing": "registry.test"
|
||||
}
|
||||
}
|
||||
count(deny) == 0 with data.semconv as {
|
||||
"registry_baseline_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": {
|
||||
"members": [{
|
||||
"id": "test",
|
||||
"value": "test",
|
||||
"stability": "stable",
|
||||
}]
|
||||
},
|
||||
}]
|
||||
}],
|
||||
"registry_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": {
|
||||
"members": [{
|
||||
"id": "test",
|
||||
"value": "test",
|
||||
"stability": "stable",
|
||||
}]
|
||||
},
|
||||
}]
|
||||
}],
|
||||
"baseline_group_ids_by_attribute": {
|
||||
"test.missing": "registry.test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Check stable attribute enum member values changing
|
||||
test_attribute_enum_member_value_change if {
|
||||
count(deny) > 0 with data.semconv as {
|
||||
"registry_baseline_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": {
|
||||
"members": [{
|
||||
"id": "test",
|
||||
"value": "test",
|
||||
"stability": "stable",
|
||||
}]
|
||||
},
|
||||
}]
|
||||
}],
|
||||
"registry_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": {
|
||||
"members": [{
|
||||
"id": "test",
|
||||
"value": "changed",
|
||||
"stability": "stable",
|
||||
}]
|
||||
},
|
||||
}]
|
||||
}],
|
||||
"baseline_group_ids_by_attribute": {
|
||||
"test.missing": "registry.test"
|
||||
}
|
||||
}
|
||||
count(deny) == 0 with data.semconv as {
|
||||
"registry_baseline_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": {
|
||||
"members": [{
|
||||
"id": "test",
|
||||
"value": "test",
|
||||
"stability": "stable",
|
||||
}]
|
||||
},
|
||||
}]
|
||||
}],
|
||||
"registry_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": {
|
||||
"members": [{
|
||||
"id": "test",
|
||||
"value": "test",
|
||||
"stability": "stable",
|
||||
}]
|
||||
},
|
||||
}]
|
||||
}],
|
||||
"baseline_group_ids_by_attribute": {
|
||||
"test.missing": "registry.test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Check stable attribute enum member values changing
|
||||
test_attribute_enum_member_missing if {
|
||||
count(deny) > 0 with data.semconv as {
|
||||
"registry_baseline_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": {
|
||||
"members": [{
|
||||
"id": "test",
|
||||
"value": "test",
|
||||
"stability": "stable",
|
||||
}, {
|
||||
"id": "missing",
|
||||
"value": "missing",
|
||||
"stability": "stable",
|
||||
}]
|
||||
},
|
||||
}]
|
||||
}],
|
||||
"registry_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": {
|
||||
"members": [{
|
||||
"id": "test",
|
||||
"value": "changed",
|
||||
"stability": "stable",
|
||||
}]
|
||||
},
|
||||
}]
|
||||
}],
|
||||
"baseline_group_ids_by_attribute": {
|
||||
"test.missing": "registry.test"
|
||||
}
|
||||
}
|
||||
count(deny) == 0 with data.semconv as {
|
||||
"registry_baseline_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": {
|
||||
"members": [{
|
||||
"id": "test",
|
||||
"value": "test",
|
||||
"stability": "stable",
|
||||
}, {
|
||||
"id": "missing",
|
||||
"value": "missing",
|
||||
"stability": "stable",
|
||||
}]
|
||||
},
|
||||
}]
|
||||
}],
|
||||
"registry_groups": [{
|
||||
"id": "registry.test",
|
||||
"type": "attribute_group",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"stability": "stable",
|
||||
"type": {
|
||||
"members": [{
|
||||
"id": "test",
|
||||
"value": "test",
|
||||
"stability": "stable",
|
||||
}, {
|
||||
"id": "missing",
|
||||
"value": "missing",
|
||||
"stability": "stable",
|
||||
}]
|
||||
},
|
||||
}]
|
||||
}],
|
||||
"baseline_group_ids_by_attribute": {
|
||||
"test.missing": "registry.test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Check that metrics cannot be removed.
|
||||
test_removed_metrics if {
|
||||
count(deny) > 0 with data.semconv as {
|
||||
"baseline_groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
}],
|
||||
}
|
||||
count(deny) == 0 with data.semconv as {
|
||||
"baseline_groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
}],
|
||||
"groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
# Check that Stable metrics cannot become unstable
|
||||
test_metric_stability_change if {
|
||||
count(deny) > 0 with data.semconv as {
|
||||
"baseline_groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
}],
|
||||
"groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "experimental",
|
||||
}]
|
||||
}
|
||||
count(deny) == 0 with data.semconv as {
|
||||
"baseline_groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
}],
|
||||
"groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
# Check that Stable metrics cannot change unit
|
||||
test_metric_unit_change if {
|
||||
count(deny) > 0 with data.semconv as {
|
||||
"baseline_groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
"unit": "s",
|
||||
}],
|
||||
"groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
"unit": "ms",
|
||||
}]
|
||||
}
|
||||
count(deny) == 0 with data.semconv as {
|
||||
"baseline_groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
"unit": "s",
|
||||
}],
|
||||
"groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
"unit": "s",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
# Check that Stable metrics cannot change unit
|
||||
test_metric_instrument_change if {
|
||||
count(deny) > 0 with data.semconv as {
|
||||
"baseline_groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
"unit": "s",
|
||||
"instrument": "histogram",
|
||||
}],
|
||||
"groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
"unit": "s",
|
||||
"instrument": "gauge",
|
||||
}]
|
||||
}
|
||||
count(deny) == 0 with data.semconv as {
|
||||
"baseline_groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
"unit": "s",
|
||||
"instrument": "histogram",
|
||||
}],
|
||||
"groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
"unit": "s",
|
||||
"instrument": "histogram",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
# Check that Stable metrics cannot change required/recommended attributes
|
||||
test_metric_attribute_missing if {
|
||||
count(deny) > 0 with data.semconv as {
|
||||
"baseline_groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
"unit": "s",
|
||||
"instrument": "histogram",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"requirement_level": "required"
|
||||
}],
|
||||
}],
|
||||
"groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
"unit": "s",
|
||||
"instrument": "gauge",
|
||||
}]
|
||||
}
|
||||
count(deny) == 0 with data.semconv as {
|
||||
"baseline_groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
"unit": "s",
|
||||
"instrument": "histogram",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"requirement_level": "required"
|
||||
},{
|
||||
"name": "test.ignored",
|
||||
"requirement_level": "opt_in"
|
||||
}],
|
||||
}],
|
||||
"groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
"unit": "s",
|
||||
"instrument": "histogram",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"requirement_level": "required"
|
||||
}],
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
# Check that Stable metrics cannot change required/recommended attributes
|
||||
test_metric_attribute_added if {
|
||||
count(deny) > 0 with data.semconv as {
|
||||
"baseline_groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
"unit": "s",
|
||||
"instrument": "histogram",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"requirement_level": "required"
|
||||
}],
|
||||
}],
|
||||
"groups": [{
|
||||
"id": "metric.test",
|
||||
"type": "metric",
|
||||
"metric_name": "test.missing",
|
||||
"stability": "stable",
|
||||
"unit": "s",
|
||||
"instrument": "gauge",
|
||||
"attributes": [{
|
||||
"name": "test.missing",
|
||||
"requirement_level": "required"
|
||||
}, {
|
||||
"name": "test.added",
|
||||
"requirement_level": "required"
|
||||
}],
|
||||
}]
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue