diff --git a/.chloggen/2477.yaml b/.chloggen/2477.yaml new file mode 100644 index 0000000000..d91cac4fc2 --- /dev/null +++ b/.chloggen/2477.yaml @@ -0,0 +1,4 @@ +change_type: bug_fix +component: cloud +note: Exclude deprecated Azure members from code generation on the `cloud.platform` attribute +issues: [2477, 2455] diff --git a/.vscode/settings.json b/.vscode/settings.json index e10a311e41..074ce8d760 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "editor.rulers": [80], "yaml.schemas": { - "https://raw.githubusercontent.com/open-telemetry/weaver/v0.15.0/schemas/semconv.schema.json": [ + "https://raw.githubusercontent.com/open-telemetry/weaver/v0.16.1/schemas/semconv.schema.json": [ "model/**/*.yaml" ] }, diff --git a/dependencies.Dockerfile b/dependencies.Dockerfile index 55a4d8a8e8..7aa10c5f84 100644 --- a/dependencies.Dockerfile +++ b/dependencies.Dockerfile @@ -3,7 +3,7 @@ # Dependabot can keep this file up to date with latest containers. # Weaver is used to generate markdown docs, and enforce policies on the model. -FROM otel/weaver:v0.15.2@sha256:b13acea09f721774daba36344861f689ac4bb8d6ecd94c4600b4d590c8fb34b9 AS weaver +FROM otel/weaver:v0.16.1@sha256:5ca4901b460217604ddb83feaca05238e2b016a226ecfb9b87a95555918a03af AS weaver # OPA is used to test policies enforced by weaver. FROM openpolicyagent/opa:1.5.1@sha256:7d30d984125161b7f30599c6bdf80a6f2301dbbd526725714c231aad8179e4b9 AS opa diff --git a/model/cloud/registry.yaml b/model/cloud/registry.yaml index 4bda5e7a44..28c8c86e45 100644 --- a/model/cloud/registry.yaml +++ b/model/cloud/registry.yaml @@ -180,36 +180,57 @@ groups: brief: Azure Virtual Machines stability: development deprecated: "Replaced by `azure.vm`" + annotations: + code_generation: + exclude: true - id: azure_container_apps value: 'azure_container_apps' brief: Azure Container Apps stability: development deprecated: "Replaced by `azure.container_apps`" + annotations: + code_generation: + exclude: true - id: azure_container_instances value: 'azure_container_instances' brief: Azure Container Instances stability: development deprecated: "Replaced by `azure.container_instances`" + annotations: + code_generation: + exclude: true - id: azure_aks value: 'azure_aks' brief: Azure Kubernetes Service stability: development deprecated: "Replaced by `azure.aks`" + annotations: + code_generation: + exclude: true - id: azure_functions value: 'azure_functions' brief: Azure Functions stability: development deprecated: "Replaced by `azure.functions`" + annotations: + code_generation: + exclude: true - id: azure_app_service value: 'azure_app_service' brief: Azure App Service stability: development deprecated: "Replaced by `azure.app_service`" + annotations: + code_generation: + exclude: true - id: azure_openshift value: 'azure_openshift' brief: Azure Red Hat OpenShift stability: development deprecated: "Replaced by `azure.openshift`" + annotations: + code_generation: + exclude: true - id: gcp_bare_metal_solution value: 'gcp_bare_metal_solution' brief: Google Bare Metal Solution (BMS) diff --git a/policies/registry.rego b/policies/registry.rego index 7067cc2c44..b1aff9e746 100644 --- a/policies/registry.rego +++ b/policies/registry.rego @@ -89,6 +89,58 @@ deny contains attr_registry_violation(description, group.id, "") if { description := sprintf("Semconv group '%s' does not contain stability field. All semconv definitions must include stability level.", [group.id]) } +# check that member ids do not collide within the same attribute +deny contains attr_registry_violation(description, group.id, attr.id) if { + group := input.groups[_] + startswith(group.id, "registry.") + + attr := group.attributes[_] + member := attr.type.members[_] + + collisions := [n | n := attr.type.members[_].id; n == member.id ] + count(collisions) > 1 + + description := sprintf("Member with id '%s' is already defined on the attribute '%s' in the group '%s'. Member id must be unique.", [member.id, attr.id, group.id]) +} + +# check that member values do not collide within the same attribute +deny contains attr_registry_violation(description, group.id, attr.id) if { + group := input.groups[_] + startswith(group.id, "registry.") + attr := group.attributes[_] + member := attr.type.members[_] + not is_property_set(member, "deprecated") + + collisions := [m + | m := attr.type.members[_] + not is_property_set(m, "deprecated") + m.value == member.value + ] + count(collisions) > 1 + + description := sprintf("Member with value '%s' (id '%s') is already defined on the attribute '%s' in the group '%s'. Member value must be unique.", [member.value, member.id, attr.id, group.id]) +} + +# check that member const names do not collide within the same attribute +deny contains attr_registry_violation(description, group.id, attr.id) if { + group := input.groups[_] + startswith(group.id, "registry.") + attr := group.attributes[_] + member := attr.type.members[_] + not member.annotations["code_generation"]["exclude"] + + const_name := to_const_name(member.id) + + collisions := [m + | m := attr.type.members[_] + to_const_name(m.id) == const_name + not m.annotations["code_generation"]["exclude"] + ] + count(collisions) > 1 + + description := sprintf("Member with const name '%s' (id '%s'), is already defined on the attribute '%s' in the group '%s'. Member const names must be unique.", [const_name, member.id, attr.id, group.id]) +} + get_attribute_name(attr, group) := name if { full_name := concat(".", [group.prefix, attr.id]) @@ -96,3 +148,10 @@ get_attribute_name(attr, group) := name if { name := trim(full_name, ".") } +to_const_name(name) = const_name if { + const_name := replace(name, ".", "_") +} + +is_property_set(obj, property) = true if { + obj[property] != null +} else = false diff --git a/policies_test/registry_test.rego b/policies_test/registry_test.rego index f0503ee837..374f144a2c 100644 --- a/policies_test/registry_test.rego +++ b/policies_test/registry_test.rego @@ -63,3 +63,44 @@ test_attribute_requirement_levels if { count(before_resolution.deny) > 0 with input as {"groups": [{"id": "registry.foo", "attributes": [{"id": "foo", "requirement_level": {"recommended": "if available"}, "stability": "rc"}]}]} count(before_resolution.deny) == 0 with input as {"groups": [{"id": "not_registry", "attributes": [{"ref": "foo", "requirement_level": "required"}]}]} } + +test_fails_on_member_id_collision if { + collision := {"groups": [ + {"id": "registry.test", "prefix": "", "attributes": [{"id": "foo.bar.baz", "type": {"members": [ + {"id": "member", "value": "value1", "brief": "brief", "stability": "stable"}, + {"id": "member", "value": "value2", "brief": "brief", "stability": "stable"}, + ]}, "stability": "stable"}]}, + ]} + count(before_resolution.deny) == 2 with input as collision +} + +test_fails_on_member_const_name_collision if { + collision := {"groups": [ + {"id": "registry.test", "prefix": "", "attributes": [{"id": "foo.bar.baz", "type": {"members": [ + {"id": "member_id", "value": "member_id", "brief": "brief", "stability": "stable"}, + {"id": "member.id", "value": "member.id", "brief": "brief", "stability": "stable"}, + ]}, "stability": "stable"}]}, + ]} + count(before_resolution.deny) == 2 with input as collision +} + +test_fails_on_member_value_collision if { + collision := {"groups": [ + {"id": "registry.test", "prefix": "", "attributes": [{"id": "foo.bar.baz", "type": {"members": [ + {"id": "member1", "value": "member", "brief": "brief", "stability": "stable"}, + {"id": "member2", "value": "member", "brief": "brief", "stability": "stable"}, + ]}, "stability": "stable"}]}, + ]} + count(before_resolution.deny) == 2 with input as collision +} + +test_passes_on_member_value_collision_with_deprecated if { + collision := {"groups": [ + {"id": "registry.test", "prefix": "", "attributes": [{"id": "foo.bar.baz", "type": {"members": [ + {"id": "member1", "value": "member", "brief": "brief", "stability": "stable", "deprecated": "renamed to member2"}, + {"id": "member2", "value": "member", "brief": "brief", "stability": "stable"}, + ]}, "stability": "stable"}]}, + ]} + count(before_resolution.deny) == 0 with input as collision +} +