Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add attribute name and const names checks #1209

Merged
merged 16 commits into from
Jul 31, 2024
Merged
7 changes: 7 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,10 @@ jobs:
run: |
make generate-gh-issue-templates
git diff --exit-code '.github/ISSUE_TEMPLATE' || (echo 'Dropdowns in issue templates is out of date, please run "make generate-gh-issue-templates" and commit the changes in this PR. Please note, if you are running it on Mac OS X, please make sure to use GNU version of sed instead of default "sed".' && exit 1)

policies-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: verify semantic conventions yaml definitions
run: make check-policies
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,11 @@ chlog-update: $(CHLOGGEN)
.PHONY: generate-gh-issue-templates
generate-gh-issue-templates:
$(TOOLS_DIR)/scripts/update-issue-template-areas.sh

.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
lmolkova marked this conversation as resolved.
Show resolved Hide resolved
49 changes: 49 additions & 0 deletions policies/attribute_name_collisions.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package after_resolution

deny[attr_registry_collision(description, name)] {
names := attr_names_except(excluded_const_collisions)
name := names[_]
const_name := to_const_name(name)
collisions:= { n | n := attr_names_except(excluded_const_collisions)[_]; n != name; to_const_name(n) == const_name }
count(collisions) > 0

description := sprintf("Attribute '%s' has the same constant name '%s' as '%s'.", [name, const_name, collisions])
}

deny[attr_registry_collision(description, name)] {
names := attr_names_except(excluded_namespace_collisions)
name := names[_]

collisions:= { n | n := input.groups[_].attributes[_].name; startswith(n, to_namespace_prefix(name)) }
count(collisions) > 0

description := sprintf("Attribute '%s' name is used as a namespace in the following attributes '%s'.", [name, collisions])
}

attr_registry_collision(description, attr_name) = violation {
violation := {
"id": description,
"type": "semconv_attribute",
"category": "naming_collision",
"attr": attr_name,
"group": "",
lmolkova marked this conversation as resolved.
Show resolved Hide resolved
}
}

to_namespace_prefix(name) = namespace {
namespace := concat("", [name, "."])
}

to_const_name(name) = const_name {
const_name := replace(name, ".", "_")
}

attr_names_except(excluded) = names {
names := { n | n := input.groups[_].attributes[_].name } - excluded
}

# TODO - we'll need to specify how collision resolution happens in the schema -
# see phase 2 in https://github.com/open-telemetry/semantic-conventions/issues/1118#issuecomment-2173803006
# For now just allow current collisions.
excluded_const_collisions := {"messaging.client_id"}
lmolkova marked this conversation as resolved.
Show resolved Hide resolved
excluded_namespace_collisions := {"messaging.operation", "db.operation", "deployment.environment"}
33 changes: 27 additions & 6 deletions policies/registry.rego
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,59 @@ package before_resolution
# used by semantic conventions.

# Helper to create attribute registry violations.
attr_registry_violation(violation_id, group_id, attr_id) = violation {
attr_registry_violation(description, group_id, attr_id) = violation {
violation := {
"id": violation_id,
"type": "semantic_convention_policies",
"id": description,
"type": "semconv_attribute",
"category": "attribute_registry_checks",
"group": group_id,
"attr": attr_id,
}
}

# We only allow attribute groups in the attribute registry.
deny[attr_registry_violation("attribute_registry_can_only_contain_attribute_groups", group.id, "")] {
deny[attr_registry_violation(description, group.id, "")] {
group := input.groups[_]
startswith(group.id, "registry.")
group.type != "attribute_group"

# TODO - separate violation_id and description once weaver supports it.
# violation_id := "attribute_registry_can_only_contain_attribute_groups"
description := sprintf("Registry group '%s' has invalid type '%s'. Groups in attribute registry must have `attribute_group` type.", [group.id, group.type])
}

# Any group that is NOT in the attribute registry that has an attribute id is
# in violation of not using the attribute registry.
deny[attr_registry_violation("attributes_must_be_defined_in_attribute_registry", group.id, attr.id)] {
deny[attr_registry_violation(description, group.id, attr.id)] {
group := input.groups[_]
not startswith(group.id, "registry.")
attr := group.attributes[_]
attr.id != null

attr_name := get_attribute_name(attr, group)

# TODO - separate violation_id and description once weaver supports it.
# violation_id := "attributes_must_be_defined_in_attribute_registry"
description := sprintf("Attribute '%s' is defined in the group '%s' which is not part of the attribute registy. Attributes can be defined in the registry group only.", [attr_name, group.id])
}

# A registry `attribute_group` containing at least one `ref` attribute is
# considered invalid if it's not in the registry group.
deny[attr_registry_violation("attributes_in_registry_cannot_reference_each_other", group.id, attr.ref)] {
deny[attr_registry_violation(description, group.id, attr.ref)] {
# TODO - this will need to be updated to support `embed` in the future.
group := input.groups[_]
startswith(group.id, "registry.")
attr := group.attributes[_]
attr.ref != null

# TODO - separate violation_id and description once weaver supports it.
# violation_id := "attributes_in_registry_cannot_reference_each_other"
description := sprintf("Registy group '%s' references attribute '%s'. Registry groups can only define new attributes.", [group.id, attr.ref])
}

get_attribute_name(attr, group) = name {
full_name = concat(".", [group.prefix, attr.id])

# if there was no prefix, we have a leading dot
name := trim(full_name, ".")
}
Loading