From d4e6a48375f986832959c9df4d64d1f9b587b13a Mon Sep 17 00:00:00 2001 From: Mateusz Hawrus Date: Fri, 14 Aug 2026 23:01:14 +0200 Subject: [PATCH 1/6] chore: document --- docs/manifest.json | 3615 +++++++++++++---- internal/cmd/objectdoc/go.mod | 8 +- internal/cmd/objectdoc/go.sum | 2 + internal/cmd/objectdoc/main.go | 632 ++- internal/cmd/objectdoc/main_test.go | 504 +++ pkg/openslo/kind.go | 3 + pkg/openslo/v1/alert_condition.go | 45 +- pkg/openslo/v1/alert_condition_test.go | 43 + pkg/openslo/v1/alert_notification_target.go | 15 +- pkg/openslo/v1/alert_policy.go | 50 +- pkg/openslo/v1/alert_policy_test.go | 22 + pkg/openslo/v1/data_source.go | 17 +- pkg/openslo/v1/doc.go | 2 +- pkg/openslo/v1/duration_shorthand.go | 34 +- pkg/openslo/v1/objects.go | 29 +- pkg/openslo/v1/service.go | 11 + pkg/openslo/v1/sli.go | 80 +- pkg/openslo/v1/sli_test.go | 32 + pkg/openslo/v1/slo.go | 149 +- pkg/openslo/v1/slo_test.go | 11 + pkg/openslo/v1alpha/doc.go | 2 +- pkg/openslo/v1alpha/examples/slos.yaml | 1 - pkg/openslo/v1alpha/objects.go | 9 +- pkg/openslo/v1alpha/service.go | 11 + pkg/openslo/v1alpha/slo.go | 116 +- pkg/openslo/v1alpha/slo_example_test.go | 3 - pkg/openslo/v1alpha/slo_test.go | 76 +- pkg/openslo/v2alpha/alert_condition.go | 46 +- pkg/openslo/v2alpha/alert_condition_test.go | 43 + .../v2alpha/alert_notification_target.go | 15 +- pkg/openslo/v2alpha/alert_policy.go | 47 +- pkg/openslo/v2alpha/alert_policy_test.go | 22 + pkg/openslo/v2alpha/data_source.go | 18 +- pkg/openslo/v2alpha/doc.go | 20 +- pkg/openslo/v2alpha/duration_shorthand.go | 35 +- pkg/openslo/v2alpha/objects.go | 18 +- pkg/openslo/v2alpha/service.go | 11 + pkg/openslo/v2alpha/sli.go | 86 +- pkg/openslo/v2alpha/sli_test.go | 36 + pkg/openslo/v2alpha/slo.go | 130 +- pkg/openslo/v2alpha/slo_test.go | 11 + pkg/openslo/version.go | 3 + 42 files changed, 4940 insertions(+), 1123 deletions(-) create mode 100644 internal/cmd/objectdoc/main_test.go diff --git a/docs/manifest.json b/docs/manifest.json index a5fcb0b..ea140d9 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -10,6 +10,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "AlertCondition defines when an SLO alert is considered breaching.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -74,6 +75,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", "childrenPaths": [ "$.metadata.name", "$.metadata.labels", @@ -104,7 +106,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object when other OpenSLO objects refer to it." }, { "path": "$.metadata.labels", @@ -113,6 +116,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Labels maps label keys to one string value each.", + "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*" @@ -166,6 +171,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Annotations maps annotation keys to arbitrary string values.", + "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -215,6 +222,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertConditionSpec defines an alert's severity and breach condition.", "childrenPaths": [ "$.spec.severity", "$.spec.condition", @@ -232,7 +240,8 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Severity is a consumer-defined alert classification." }, { "path": "$.spec.condition", @@ -247,6 +256,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertConditionType defines a burn-rate comparison over a lookback window. Burn rate is error-budget consumption relative to the rate allowed by the SLO.", "childrenPaths": [ "$.spec.condition.kind", "$.spec.condition.op", @@ -274,7 +284,9 @@ "description": "must be one of: burnrate", "errorCode": "one_of" } - ] + ], + "typeDoc": "AlertConditionKind identifies the evaluation algorithm for an [AlertConditionType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionType).", + "fieldDoc": "Kind selects the condition algorithm. OpenSLO defaults omitted values to [AlertConditionKindBurnRate](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionKindBurnRate). This SDK does not apply that default." }, { "path": "$.spec.condition.op", @@ -292,13 +304,21 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] }, { "description": "must be one of: gt, lt, gte, lte", - "errorCode": "one_of" + "errorCode": "one_of", + "conditions": [ + "'kind' is 'burnrate'" + ] } - ] + ], + "typeDoc": "Operator specifies a comparison operation for an SLO objective or alert condition.", + "fieldDoc": "Operator compares the calculated burn rate with Threshold." }, { "path": "$.spec.condition.threshold", @@ -309,9 +329,13 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] } - ] + ], + "fieldDoc": "Threshold sets the numeric burn-rate boundary." }, { "path": "$.spec.condition.lookbackWindow", @@ -323,10 +347,14 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] } ], - "typeDoc": "DurationShorthand is a shorthand representation of time duration. It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days)." + "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", + "fieldDoc": "LookbackWindow sets the period for burn-rate calculation." }, { "path": "$.spec.condition.alertAfter", @@ -338,10 +366,14 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] } ], - "typeDoc": "DurationShorthand is a shorthand representation of time duration. It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days)." + "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", + "fieldDoc": "AlertAfter sets how long the comparison must hold before the condition breaches. OpenSLO defaults omitted values to \"0m\". This SDK does not apply that default." }, { "path": "$.spec.description", @@ -354,7 +386,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the alert condition." } ] }, @@ -368,6 +401,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "AlertNotificationTarget names a destination for alert delivery.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -432,6 +466,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", "childrenPaths": [ "$.metadata.name", "$.metadata.labels", @@ -462,7 +497,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object when other OpenSLO objects refer to it." }, { "path": "$.metadata.labels", @@ -471,6 +507,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Labels maps label keys to one string value each.", + "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*" @@ -524,6 +562,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Annotations maps annotation keys to arbitrary string values.", + "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -573,6 +613,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertNotificationTargetSpec identifies the consumer-defined notification destination.", "childrenPaths": [ "$.spec.description", "$.spec.target" @@ -589,7 +630,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the destination." }, { "path": "$.spec.target", @@ -602,7 +644,8 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Target identifies the consumer-defined destination for alert delivery." } ] }, @@ -616,6 +659,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "AlertPolicy defines when the system emits an SLO alert and where it sends it.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -680,6 +724,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", "childrenPaths": [ "$.metadata.name", "$.metadata.labels", @@ -710,7 +755,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object when other OpenSLO objects refer to it." }, { "path": "$.metadata.labels", @@ -719,6 +765,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Labels maps label keys to one string value each.", + "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*" @@ -772,6 +820,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Annotations maps annotation keys to arbitrary string values.", + "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -815,6 +865,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "AlertPolicySpec defines the trigger states, condition, and notification destinations for an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy).", "childrenPaths": [ "$.spec.description", "$.spec.alertWhenNoData", @@ -837,28 +888,32 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the alert policy." }, { "path": "$.spec.alertWhenNoData", "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "AlertWhenNoData enables notifications when the associated SLO has no burn-rate value." }, { "path": "$.spec.alertWhenBreaching", "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "AlertWhenBreaching enables notifications when the condition starts breaching." }, { "path": "$.spec.alertWhenResolved", "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "AlertWhenResolved enables notifications when the condition resolves." }, { "path": "$.spec.conditions", @@ -872,7 +927,8 @@ "description": "length must be between 1 and 1", "errorCode": "slice_length" } - ] + ], + "fieldDoc": "Conditions supplies the policy's alert condition by reference or inline." }, { "path": "$.spec.conditions[*]", @@ -883,7 +939,7 @@ }, "rules": [ { - "description": "properties are mutually exclusive: conditionRef, spec", + "description": "exactly one of 'conditionRef' and 'spec' must be set", "errorCode": "mutually_exclusive" }, { @@ -891,6 +947,7 @@ "errorCode": "optional" } ], + "typeDoc": "AlertPolicyCondition supplies a condition to an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy) by reference or inline definition.", "childrenPaths": [ "$.spec.conditions[*].conditionRef", "$.spec.conditions[*].kind", @@ -922,7 +979,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "ConditionRef names the alert condition to use." }, { "path": "$.spec.conditions[*].kind", @@ -959,6 +1017,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", "childrenPaths": [ "$.spec.conditions[*].metadata.name", "$.spec.conditions[*].metadata.labels", @@ -989,7 +1048,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object when other OpenSLO objects refer to it." }, { "path": "$.spec.conditions[*].metadata.labels", @@ -998,6 +1058,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Labels maps label keys to one string value each.", + "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.spec.conditions[*].metadata.labels.*~", "$.spec.conditions[*].metadata.labels.*" @@ -1051,6 +1113,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Annotations maps annotation keys to arbitrary string values.", + "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.spec.conditions[*].metadata.annotations.*~", "$.spec.conditions[*].metadata.annotations.*" @@ -1100,6 +1164,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertConditionSpec defines an alert's severity and breach condition.", "childrenPaths": [ "$.spec.conditions[*].spec.severity", "$.spec.conditions[*].spec.condition", @@ -1117,7 +1182,8 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Severity is a consumer-defined alert classification." }, { "path": "$.spec.conditions[*].spec.condition", @@ -1132,6 +1198,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertConditionType defines a burn-rate comparison over a lookback window. Burn rate is error-budget consumption relative to the rate allowed by the SLO.", "childrenPaths": [ "$.spec.conditions[*].spec.condition.kind", "$.spec.conditions[*].spec.condition.op", @@ -1159,7 +1226,9 @@ "description": "must be one of: burnrate", "errorCode": "one_of" } - ] + ], + "typeDoc": "AlertConditionKind identifies the evaluation algorithm for an [AlertConditionType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionType).", + "fieldDoc": "Kind selects the condition algorithm. OpenSLO defaults omitted values to [AlertConditionKindBurnRate](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionKindBurnRate). This SDK does not apply that default." }, { "path": "$.spec.conditions[*].spec.condition.op", @@ -1177,13 +1246,21 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] }, { "description": "must be one of: gt, lt, gte, lte", - "errorCode": "one_of" + "errorCode": "one_of", + "conditions": [ + "'kind' is 'burnrate'" + ] } - ] + ], + "typeDoc": "Operator specifies a comparison operation for an SLO objective or alert condition.", + "fieldDoc": "Operator compares the calculated burn rate with Threshold." }, { "path": "$.spec.conditions[*].spec.condition.threshold", @@ -1194,9 +1271,13 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] } - ] + ], + "fieldDoc": "Threshold sets the numeric burn-rate boundary." }, { "path": "$.spec.conditions[*].spec.condition.lookbackWindow", @@ -1208,10 +1289,14 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] } ], - "typeDoc": "DurationShorthand is a shorthand representation of time duration. It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days)." + "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", + "fieldDoc": "LookbackWindow sets the period for burn-rate calculation." }, { "path": "$.spec.conditions[*].spec.condition.alertAfter", @@ -1223,10 +1308,14 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] } ], - "typeDoc": "DurationShorthand is a shorthand representation of time duration. It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days)." + "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", + "fieldDoc": "AlertAfter sets how long the comparison must hold before the condition breaches. OpenSLO defaults omitted values to \"0m\". This SDK does not apply that default." }, { "path": "$.spec.conditions[*].spec.description", @@ -1239,7 +1328,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the alert condition." }, { "path": "$.spec.notificationTargets", @@ -1253,7 +1343,8 @@ "description": "length must be greater than or equal to 1", "errorCode": "slice_min_length" } - ] + ], + "fieldDoc": "NotificationTargets lists referenced or inline delivery destinations." }, { "path": "$.spec.notificationTargets[*]", @@ -1264,7 +1355,7 @@ }, "rules": [ { - "description": "properties are mutually exclusive: spec, targetRef", + "description": "exactly one of 'targetRef' and 'spec' must be set", "errorCode": "mutually_exclusive" }, { @@ -1272,6 +1363,7 @@ "errorCode": "optional" } ], + "typeDoc": "AlertPolicyNotificationTarget supplies a notification destination to an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy) by reference or inline definition.", "childrenPaths": [ "$.spec.notificationTargets[*].targetRef", "$.spec.notificationTargets[*].kind", @@ -1303,7 +1395,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "TargetRef names the notification target to use." }, { "path": "$.spec.notificationTargets[*].kind", @@ -1340,6 +1433,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", "childrenPaths": [ "$.spec.notificationTargets[*].metadata.name", "$.spec.notificationTargets[*].metadata.labels", @@ -1370,7 +1464,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object when other OpenSLO objects refer to it." }, { "path": "$.spec.notificationTargets[*].metadata.labels", @@ -1379,6 +1474,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Labels maps label keys to one string value each.", + "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.spec.notificationTargets[*].metadata.labels.*~", "$.spec.notificationTargets[*].metadata.labels.*" @@ -1432,6 +1529,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Annotations maps annotation keys to arbitrary string values.", + "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.spec.notificationTargets[*].metadata.annotations.*~", "$.spec.notificationTargets[*].metadata.annotations.*" @@ -1481,6 +1580,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertNotificationTargetSpec identifies the consumer-defined notification destination.", "childrenPaths": [ "$.spec.notificationTargets[*].spec.description", "$.spec.notificationTargets[*].spec.target" @@ -1497,7 +1597,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the destination." }, { "path": "$.spec.notificationTargets[*].spec.target", @@ -1510,7 +1611,8 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Target identifies the consumer-defined destination for alert delivery." } ] }, @@ -1524,6 +1626,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "DataSource makes metric-source connection details reusable across SLIs.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -1588,6 +1691,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", "childrenPaths": [ "$.metadata.name", "$.metadata.labels", @@ -1618,7 +1722,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object when other OpenSLO objects refer to it." }, { "path": "$.metadata.labels", @@ -1627,6 +1732,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Labels maps label keys to one string value each.", + "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*" @@ -1680,6 +1787,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Annotations maps annotation keys to arbitrary string values.", + "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -1729,11 +1838,11 @@ "errorCode": "required" } ], + "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", "childrenPaths": [ "$.spec.description", "$.spec.type", - "$.spec.connectionDetails", - "$.spec.connectionDetails[*]" + "$.spec.connectionDetails" ] }, { @@ -1747,7 +1856,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.type", @@ -1760,13 +1870,14 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Type identifies the data-source implementation understood by the consumer." }, { "path": "$.spec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ @@ -1774,14 +1885,8 @@ "description": "property is required", "errorCode": "required" } - ] - }, - { - "path": "$.spec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." } ] }, @@ -1795,6 +1900,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "SLI describes how to read a metric from a data source, as defined by the [OpenSLO v2alpha SLI](https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md#sli).", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -1859,6 +1965,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", "childrenPaths": [ "$.metadata.name", "$.metadata.labels", @@ -1889,7 +1996,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object when other OpenSLO objects refer to it." }, { "path": "$.metadata.labels", @@ -1898,6 +2006,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Labels maps label keys to one string value each.", + "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*" @@ -1951,6 +2061,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Annotations maps annotation keys to arbitrary string values.", + "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -1996,10 +2108,11 @@ }, "rules": [ { - "description": "properties are mutually exclusive: ratioMetric, thresholdMetric", + "description": "exactly one of 'thresholdMetric' and 'ratioMetric' must be set", "errorCode": "mutually_exclusive" } ], + "typeDoc": "SLISpec defines the metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLI).", "childrenPaths": [ "$.spec.description", "$.spec.thresholdMetric", @@ -2017,7 +2130,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the indicator." }, { "path": "$.spec.thresholdMetric", @@ -2032,10 +2146,12 @@ "errorCode": "optional" }, { - "description": "properties are mutually exclusive: dataSourceRef, dataSourceSpec", + "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", "errorCode": "mutually_exclusive" } ], + "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "fieldDoc": "ThresholdMetric supplies values for [SLOObjective.Operator](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Operator) to compare with [SLOObjective.Value](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Value).", "childrenPaths": [ "$.spec.thresholdMetric.dataSourceRef", "$.spec.thresholdMetric.dataSourceSpec", @@ -2066,7 +2182,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "DataSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#DataSource)." }, { "path": "$.spec.thresholdMetric.dataSourceSpec", @@ -2081,11 +2198,12 @@ "errorCode": "optional" } ], + "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", + "fieldDoc": "DataSourceSpec embeds the data-source configuration.", "childrenPaths": [ "$.spec.thresholdMetric.dataSourceSpec.description", "$.spec.thresholdMetric.dataSourceSpec.type", - "$.spec.thresholdMetric.dataSourceSpec.connectionDetails", - "$.spec.thresholdMetric.dataSourceSpec.connectionDetails[*]" + "$.spec.thresholdMetric.dataSourceSpec.connectionDetails" ] }, { @@ -2099,7 +2217,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.thresholdMetric.dataSourceSpec.type", @@ -2112,13 +2231,14 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Type identifies the data-source implementation understood by the consumer." }, { "path": "$.spec.thresholdMetric.dataSourceSpec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ @@ -2126,14 +2246,8 @@ "description": "property is required", "errorCode": "required" } - ] - }, - { - "path": "$.spec.thresholdMetric.dataSourceSpec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." }, { "path": "$.spec.thresholdMetric.spec", @@ -2141,6 +2255,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, + "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.thresholdMetric.spec.*~", "$.spec.thresholdMetric.spec.*" @@ -2173,7 +2288,7 @@ "errorCode": "optional" }, { - "description": "properties are mutually exclusive: raw, total", + "description": "exactly one of 'total' and 'raw' must be set", "errorCode": "mutually_exclusive" }, { @@ -2182,9 +2297,14 @@ }, { "description": "one of [bad, good] properties must be set, none was provided", - "errorCode": "one_of_properties" + "errorCode": "one_of_properties", + "conditions": [ + "'total' is set" + ] } ], + "typeDoc": "SLIRatioMetric derives a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 990 good events out of 1,000 total events produce \"0.99\". 10 bad events with the same total produce the same success ratio.", + "fieldDoc": "RatioMetric supplies a success ratio for an SLO objective.", "childrenPaths": [ "$.spec.ratioMetric.counter", "$.spec.ratioMetric.good", @@ -2199,7 +2319,8 @@ "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "Counter reports whether the good, bad, and total metrics are monotonically increasing counters. It has no effect when Raw is used." }, { "path": "$.spec.ratioMetric.good", @@ -2211,13 +2332,23 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'total' is set", + "'good' is set" + ] }, { - "description": "properties are mutually exclusive: dataSourceRef, dataSourceSpec", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "'total' is set", + "'good' is set" + ] } ], + "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "fieldDoc": "Good is the success-count numerator used with Total.", "childrenPaths": [ "$.spec.ratioMetric.good.dataSourceRef", "$.spec.ratioMetric.good.dataSourceSpec", @@ -2233,22 +2364,35 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'total' is set", + "'good' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'total' is set", + "'good' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'total' is set", + "'good' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "DataSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#DataSource)." }, { "path": "$.spec.ratioMetric.good.dataSourceSpec", @@ -2260,14 +2404,19 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'total' is set", + "'good' is set" + ] } ], + "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", + "fieldDoc": "DataSourceSpec embeds the data-source configuration.", "childrenPaths": [ "$.spec.ratioMetric.good.dataSourceSpec.description", "$.spec.ratioMetric.good.dataSourceSpec.type", - "$.spec.ratioMetric.good.dataSourceSpec.connectionDetails", - "$.spec.ratioMetric.good.dataSourceSpec.connectionDetails[*]" + "$.spec.ratioMetric.good.dataSourceSpec.connectionDetails" ] }, { @@ -2279,9 +2428,14 @@ "rules": [ { "description": "length must be less than or equal to 1050", - "errorCode": "string_max_length" + "errorCode": "string_max_length", + "conditions": [ + "'total' is set", + "'good' is set" + ] } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.ratioMetric.good.dataSourceSpec.type", @@ -2292,30 +2446,33 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'total' is set", + "'good' is set" + ] } - ] + ], + "fieldDoc": "Type identifies the data-source implementation understood by the consumer." }, { "path": "$.spec.ratioMetric.good.dataSourceSpec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'total' is set", + "'good' is set" + ] } - ] - }, - { - "path": "$.spec.ratioMetric.good.dataSourceSpec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." }, { "path": "$.spec.ratioMetric.good.spec", @@ -2323,6 +2480,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, + "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.ratioMetric.good.spec.*~", "$.spec.ratioMetric.good.spec.*" @@ -2352,13 +2510,23 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'total' is set", + "'bad' is set" + ] }, { - "description": "properties are mutually exclusive: dataSourceRef, dataSourceSpec", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "'total' is set", + "'bad' is set" + ] } ], + "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "fieldDoc": "Bad is the failure-count input used with Total to derive successes.", "childrenPaths": [ "$.spec.ratioMetric.bad.dataSourceRef", "$.spec.ratioMetric.bad.dataSourceSpec", @@ -2374,22 +2542,35 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'total' is set", + "'bad' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'total' is set", + "'bad' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'total' is set", + "'bad' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "DataSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#DataSource)." }, { "path": "$.spec.ratioMetric.bad.dataSourceSpec", @@ -2401,14 +2582,19 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'total' is set", + "'bad' is set" + ] } ], + "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", + "fieldDoc": "DataSourceSpec embeds the data-source configuration.", "childrenPaths": [ "$.spec.ratioMetric.bad.dataSourceSpec.description", "$.spec.ratioMetric.bad.dataSourceSpec.type", - "$.spec.ratioMetric.bad.dataSourceSpec.connectionDetails", - "$.spec.ratioMetric.bad.dataSourceSpec.connectionDetails[*]" + "$.spec.ratioMetric.bad.dataSourceSpec.connectionDetails" ] }, { @@ -2420,9 +2606,14 @@ "rules": [ { "description": "length must be less than or equal to 1050", - "errorCode": "string_max_length" + "errorCode": "string_max_length", + "conditions": [ + "'total' is set", + "'bad' is set" + ] } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.ratioMetric.bad.dataSourceSpec.type", @@ -2433,30 +2624,33 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'total' is set", + "'bad' is set" + ] } - ] + ], + "fieldDoc": "Type identifies the data-source implementation understood by the consumer." }, { "path": "$.spec.ratioMetric.bad.dataSourceSpec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'total' is set", + "'bad' is set" + ] } - ] - }, - { - "path": "$.spec.ratioMetric.bad.dataSourceSpec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." }, { "path": "$.spec.ratioMetric.bad.spec", @@ -2464,6 +2658,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, + "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.ratioMetric.bad.spec.*~", "$.spec.ratioMetric.bad.spec.*" @@ -2493,13 +2688,21 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'total' is set" + ] }, { - "description": "properties are mutually exclusive: dataSourceRef, dataSourceSpec", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "'total' is set" + ] } ], + "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "fieldDoc": "Total is the denominator paired with Good or Bad.", "childrenPaths": [ "$.spec.ratioMetric.total.dataSourceRef", "$.spec.ratioMetric.total.dataSourceSpec", @@ -2515,22 +2718,32 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'total' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'total' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'total' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "DataSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#DataSource)." }, { "path": "$.spec.ratioMetric.total.dataSourceSpec", @@ -2542,14 +2755,18 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'total' is set" + ] } ], + "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", + "fieldDoc": "DataSourceSpec embeds the data-source configuration.", "childrenPaths": [ "$.spec.ratioMetric.total.dataSourceSpec.description", "$.spec.ratioMetric.total.dataSourceSpec.type", - "$.spec.ratioMetric.total.dataSourceSpec.connectionDetails", - "$.spec.ratioMetric.total.dataSourceSpec.connectionDetails[*]" + "$.spec.ratioMetric.total.dataSourceSpec.connectionDetails" ] }, { @@ -2561,9 +2778,13 @@ "rules": [ { "description": "length must be less than or equal to 1050", - "errorCode": "string_max_length" + "errorCode": "string_max_length", + "conditions": [ + "'total' is set" + ] } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.ratioMetric.total.dataSourceSpec.type", @@ -2574,30 +2795,31 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'total' is set" + ] } - ] + ], + "fieldDoc": "Type identifies the data-source implementation understood by the consumer." }, { "path": "$.spec.ratioMetric.total.dataSourceSpec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'total' is set" + ] } - ] - }, - { - "path": "$.spec.ratioMetric.total.dataSourceSpec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." }, { "path": "$.spec.ratioMetric.total.spec", @@ -2605,6 +2827,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, + "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.ratioMetric.total.spec.*~", "$.spec.ratioMetric.total.spec.*" @@ -2638,13 +2861,21 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'raw' is set" + ] }, { "description": "must be one of: success, failure", - "errorCode": "one_of" + "errorCode": "one_of", + "conditions": [ + "'raw' is set" + ] } - ] + ], + "typeDoc": "SLIRawMetricType identifies whether a raw ratio contains successes (good/total) or failures (bad/total).", + "fieldDoc": "RawType identifies whether Raw contains a success or failure ratio when Raw is used." }, { "path": "$.spec.ratioMetric.raw", @@ -2656,13 +2887,21 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'raw' is set" + ] }, { - "description": "properties are mutually exclusive: dataSourceRef, dataSourceSpec", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "'raw' is set" + ] } ], + "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "fieldDoc": "Raw supplies an already computed ratio.", "childrenPaths": [ "$.spec.ratioMetric.raw.dataSourceRef", "$.spec.ratioMetric.raw.dataSourceSpec", @@ -2678,22 +2917,32 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'raw' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'raw' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'raw' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "DataSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#DataSource)." }, { "path": "$.spec.ratioMetric.raw.dataSourceSpec", @@ -2705,14 +2954,18 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'raw' is set" + ] } ], + "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", + "fieldDoc": "DataSourceSpec embeds the data-source configuration.", "childrenPaths": [ "$.spec.ratioMetric.raw.dataSourceSpec.description", "$.spec.ratioMetric.raw.dataSourceSpec.type", - "$.spec.ratioMetric.raw.dataSourceSpec.connectionDetails", - "$.spec.ratioMetric.raw.dataSourceSpec.connectionDetails[*]" + "$.spec.ratioMetric.raw.dataSourceSpec.connectionDetails" ] }, { @@ -2724,9 +2977,13 @@ "rules": [ { "description": "length must be less than or equal to 1050", - "errorCode": "string_max_length" + "errorCode": "string_max_length", + "conditions": [ + "'raw' is set" + ] } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.ratioMetric.raw.dataSourceSpec.type", @@ -2737,30 +2994,31 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'raw' is set" + ] } - ] + ], + "fieldDoc": "Type identifies the data-source implementation understood by the consumer." }, { "path": "$.spec.ratioMetric.raw.dataSourceSpec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'raw' is set" + ] } - ] - }, - { - "path": "$.spec.ratioMetric.raw.dataSourceSpec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." }, { "path": "$.spec.ratioMetric.raw.spec", @@ -2768,6 +3026,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, + "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.ratioMetric.raw.spec.*~", "$.spec.ratioMetric.raw.spec.*" @@ -2799,6 +3058,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "SLO defines a target for an SLI over a time window, as specified by the [OpenSLO v2alpha SLO](https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md#slo).", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -2863,6 +3123,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", "childrenPaths": [ "$.metadata.name", "$.metadata.labels", @@ -2893,7 +3154,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object when other OpenSLO objects refer to it." }, { "path": "$.metadata.labels", @@ -2902,6 +3164,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Labels maps label keys to one string value each.", + "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*" @@ -2955,6 +3219,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Annotations maps annotation keys to arbitrary string values.", + "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -3008,10 +3274,14 @@ "errorCode": "mutually_exclusive" }, { - "description": "properties are mutually exclusive: sli, sliRef", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'sli' and 'sliRef' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "'sli' or 'sliRef' is set" + ] } ], + "typeDoc": "SLOSpec defines an SLO's service, SLI, time window, budgeting method, objectives, and alert policies. A standard SLO applies one SLI to all objectives. A composite SLO can select a different SLI per objective.", "childrenPaths": [ "$.spec.description", "$.spec.serviceRef", @@ -3037,7 +3307,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the SLO." }, { "path": "$.spec.serviceRef", @@ -3050,7 +3321,8 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "ServiceRef names the service associated with this SLO. The SDK serializes the field as \"serviceRef\". The living v2alpha proposal calls it \"service\"." }, { "path": "$.spec.sli", @@ -3062,9 +3334,14 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set" + ] } ], + "typeDoc": "SLOSLIInline embeds an SLI definition in an SLO or one of its objectives.", + "fieldDoc": "SLI embeds the service level indicator for a standard SLO.", "childrenPaths": [ "$.spec.sli.metadata", "$.spec.sli.spec" @@ -3080,9 +3357,13 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'sli' or 'sliRef' is set" + ] } ], + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", "childrenPaths": [ "$.spec.sli.metadata.name", "$.spec.sli.metadata.labels", @@ -3098,22 +3379,32 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'sli' or 'sliRef' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'sli' or 'sliRef' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'sli' or 'sliRef' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object when other OpenSLO objects refer to it." }, { "path": "$.spec.sli.metadata.labels", @@ -3122,6 +3413,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Labels maps label keys to one string value each.", + "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.spec.sli.metadata.labels.*~", "$.spec.sli.metadata.labels.*" @@ -3136,12 +3429,18 @@ "rules": [ { "description": "length must be between 1 and 317", - "errorCode": "string_kubernetes_qualified_name:string_length" + "errorCode": "string_kubernetes_qualified_name:string_length", + "conditions": [ + "'sli' or 'sliRef' is set" + ] }, { "description": "string must be a Kubernetes Qualified Name", "details": "Kubernetes Qualified Name must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character with an optional DNS subdomain prefix and '/'", "errorCode": "string_kubernetes_qualified_name", + "conditions": [ + "'sli' or 'sliRef' is set" + ], "examples": [ "my.domain/MyName", "MyName", @@ -3161,6 +3460,9 @@ { "description": "string must match regular expression: '^([a-z0-9]([-._a-z0-9]{0,61}[a-z0-9])?)?$'", "errorCode": "string_match_regexp", + "conditions": [ + "'sli' or 'sliRef' is set" + ], "examples": [ "my-label", "my.domain_123-label" @@ -3175,6 +3477,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Annotations maps annotation keys to arbitrary string values.", + "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.spec.sli.metadata.annotations.*~", "$.spec.sli.metadata.annotations.*" @@ -3189,12 +3493,18 @@ "rules": [ { "description": "length must be between 1 and 317", - "errorCode": "string_kubernetes_qualified_name:string_length" + "errorCode": "string_kubernetes_qualified_name:string_length", + "conditions": [ + "'sli' or 'sliRef' is set" + ] }, { "description": "string must be a Kubernetes Qualified Name", "details": "Kubernetes Qualified Name must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character with an optional DNS subdomain prefix and '/'", "errorCode": "string_kubernetes_qualified_name", + "conditions": [ + "'sli' or 'sliRef' is set" + ], "examples": [ "my.domain/MyName", "MyName", @@ -3220,10 +3530,14 @@ }, "rules": [ { - "description": "properties are mutually exclusive: ratioMetric, thresholdMetric", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'thresholdMetric' and 'ratioMetric' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "'sli' or 'sliRef' is set" + ] } ], + "typeDoc": "SLISpec defines the metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLI).", "childrenPaths": [ "$.spec.sli.spec.description", "$.spec.sli.spec.thresholdMetric", @@ -3239,9 +3553,13 @@ "rules": [ { "description": "length must be less than or equal to 1050", - "errorCode": "string_max_length" + "errorCode": "string_max_length", + "conditions": [ + "'sli' or 'sliRef' is set" + ] } - ] + ], + "fieldDoc": "Description summarizes the indicator." }, { "path": "$.spec.sli.spec.thresholdMetric", @@ -3253,13 +3571,21 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set" + ] }, { - "description": "properties are mutually exclusive: dataSourceRef, dataSourceSpec", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "'sli' or 'sliRef' is set" + ] } ], + "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "fieldDoc": "ThresholdMetric supplies values for [SLOObjective.Operator](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Operator) to compare with [SLOObjective.Value](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Value).", "childrenPaths": [ "$.spec.sli.spec.thresholdMetric.dataSourceRef", "$.spec.sli.spec.thresholdMetric.dataSourceSpec", @@ -3275,22 +3601,32 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'sli' or 'sliRef' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'sli' or 'sliRef' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "DataSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#DataSource)." }, { "path": "$.spec.sli.spec.thresholdMetric.dataSourceSpec", @@ -3302,14 +3638,18 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set" + ] } ], + "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", + "fieldDoc": "DataSourceSpec embeds the data-source configuration.", "childrenPaths": [ "$.spec.sli.spec.thresholdMetric.dataSourceSpec.description", "$.spec.sli.spec.thresholdMetric.dataSourceSpec.type", - "$.spec.sli.spec.thresholdMetric.dataSourceSpec.connectionDetails", - "$.spec.sli.spec.thresholdMetric.dataSourceSpec.connectionDetails[*]" + "$.spec.sli.spec.thresholdMetric.dataSourceSpec.connectionDetails" ] }, { @@ -3321,9 +3661,13 @@ "rules": [ { "description": "length must be less than or equal to 1050", - "errorCode": "string_max_length" + "errorCode": "string_max_length", + "conditions": [ + "'sli' or 'sliRef' is set" + ] } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.sli.spec.thresholdMetric.dataSourceSpec.type", @@ -3334,30 +3678,31 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'sli' or 'sliRef' is set" + ] } - ] + ], + "fieldDoc": "Type identifies the data-source implementation understood by the consumer." }, { "path": "$.spec.sli.spec.thresholdMetric.dataSourceSpec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'sli' or 'sliRef' is set" + ] } - ] - }, - { - "path": "$.spec.sli.spec.thresholdMetric.dataSourceSpec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." }, { "path": "$.spec.sli.spec.thresholdMetric.spec", @@ -3365,6 +3710,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, + "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.sli.spec.thresholdMetric.spec.*~", "$.spec.sli.spec.thresholdMetric.spec.*" @@ -3394,21 +3740,36 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set" + ] }, { - "description": "properties are mutually exclusive: raw, total", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'total' and 'raw' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "'sli' or 'sliRef' is set" + ] }, { "description": "properties are mutually exclusive: bad, good, raw", - "errorCode": "mutually_exclusive" + "errorCode": "mutually_exclusive", + "conditions": [ + "'sli' or 'sliRef' is set" + ] }, { "description": "one of [bad, good] properties must be set, none was provided", - "errorCode": "one_of_properties" + "errorCode": "one_of_properties", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set" + ] } ], + "typeDoc": "SLIRatioMetric derives a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 990 good events out of 1,000 total events produce \"0.99\". 10 bad events with the same total produce the same success ratio.", + "fieldDoc": "RatioMetric supplies a success ratio for an SLO objective.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.counter", "$.spec.sli.spec.ratioMetric.good", @@ -3423,7 +3784,8 @@ "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "Counter reports whether the good, bad, and total metrics are monotonically increasing counters. It has no effect when Raw is used." }, { "path": "$.spec.sli.spec.ratioMetric.good", @@ -3435,13 +3797,25 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'good' is set" + ] }, { - "description": "properties are mutually exclusive: dataSourceRef, dataSourceSpec", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'good' is set" + ] } ], + "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "fieldDoc": "Good is the success-count numerator used with Total.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.good.dataSourceRef", "$.spec.sli.spec.ratioMetric.good.dataSourceSpec", @@ -3457,22 +3831,38 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'good' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'good' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'good' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "DataSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#DataSource)." }, { "path": "$.spec.sli.spec.ratioMetric.good.dataSourceSpec", @@ -3484,14 +3874,20 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'good' is set" + ] } ], + "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", + "fieldDoc": "DataSourceSpec embeds the data-source configuration.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.good.dataSourceSpec.description", "$.spec.sli.spec.ratioMetric.good.dataSourceSpec.type", - "$.spec.sli.spec.ratioMetric.good.dataSourceSpec.connectionDetails", - "$.spec.sli.spec.ratioMetric.good.dataSourceSpec.connectionDetails[*]" + "$.spec.sli.spec.ratioMetric.good.dataSourceSpec.connectionDetails" ] }, { @@ -3503,9 +3899,15 @@ "rules": [ { "description": "length must be less than or equal to 1050", - "errorCode": "string_max_length" + "errorCode": "string_max_length", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'good' is set" + ] } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.sli.spec.ratioMetric.good.dataSourceSpec.type", @@ -3516,30 +3918,35 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'good' is set" + ] } - ] + ], + "fieldDoc": "Type identifies the data-source implementation understood by the consumer." }, { "path": "$.spec.sli.spec.ratioMetric.good.dataSourceSpec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'good' is set" + ] } - ] - }, - { - "path": "$.spec.sli.spec.ratioMetric.good.dataSourceSpec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." }, { "path": "$.spec.sli.spec.ratioMetric.good.spec", @@ -3547,6 +3954,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, + "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.good.spec.*~", "$.spec.sli.spec.ratioMetric.good.spec.*" @@ -3576,13 +3984,25 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" + ] }, { - "description": "properties are mutually exclusive: dataSourceRef, dataSourceSpec", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" + ] } ], + "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "fieldDoc": "Bad is the failure-count input used with Total to derive successes.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.bad.dataSourceRef", "$.spec.sli.spec.ratioMetric.bad.dataSourceSpec", @@ -3598,22 +4018,38 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "DataSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#DataSource)." }, { "path": "$.spec.sli.spec.ratioMetric.bad.dataSourceSpec", @@ -3625,14 +4061,20 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" + ] } ], + "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", + "fieldDoc": "DataSourceSpec embeds the data-source configuration.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.bad.dataSourceSpec.description", "$.spec.sli.spec.ratioMetric.bad.dataSourceSpec.type", - "$.spec.sli.spec.ratioMetric.bad.dataSourceSpec.connectionDetails", - "$.spec.sli.spec.ratioMetric.bad.dataSourceSpec.connectionDetails[*]" + "$.spec.sli.spec.ratioMetric.bad.dataSourceSpec.connectionDetails" ] }, { @@ -3644,9 +4086,15 @@ "rules": [ { "description": "length must be less than or equal to 1050", - "errorCode": "string_max_length" + "errorCode": "string_max_length", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" + ] } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.sli.spec.ratioMetric.bad.dataSourceSpec.type", @@ -3657,30 +4105,35 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" + ] } - ] + ], + "fieldDoc": "Type identifies the data-source implementation understood by the consumer." }, { "path": "$.spec.sli.spec.ratioMetric.bad.dataSourceSpec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" + ] } - ] - }, - { - "path": "$.spec.sli.spec.ratioMetric.bad.dataSourceSpec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." }, { "path": "$.spec.sli.spec.ratioMetric.bad.spec", @@ -3688,6 +4141,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, + "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.bad.spec.*~", "$.spec.sli.spec.ratioMetric.bad.spec.*" @@ -3717,13 +4171,23 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set" + ] }, { - "description": "properties are mutually exclusive: dataSourceRef, dataSourceSpec", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set" + ] } ], + "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "fieldDoc": "Total is the denominator paired with Good or Bad.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.total.dataSourceRef", "$.spec.sli.spec.ratioMetric.total.dataSourceSpec", @@ -3739,22 +4203,35 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "DataSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#DataSource)." }, { "path": "$.spec.sli.spec.ratioMetric.total.dataSourceSpec", @@ -3766,14 +4243,19 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set" + ] } ], + "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", + "fieldDoc": "DataSourceSpec embeds the data-source configuration.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.total.dataSourceSpec.description", "$.spec.sli.spec.ratioMetric.total.dataSourceSpec.type", - "$.spec.sli.spec.ratioMetric.total.dataSourceSpec.connectionDetails", - "$.spec.sli.spec.ratioMetric.total.dataSourceSpec.connectionDetails[*]" + "$.spec.sli.spec.ratioMetric.total.dataSourceSpec.connectionDetails" ] }, { @@ -3785,9 +4267,14 @@ "rules": [ { "description": "length must be less than or equal to 1050", - "errorCode": "string_max_length" + "errorCode": "string_max_length", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set" + ] } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.sli.spec.ratioMetric.total.dataSourceSpec.type", @@ -3798,30 +4285,33 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set" + ] } - ] + ], + "fieldDoc": "Type identifies the data-source implementation understood by the consumer." }, { "path": "$.spec.sli.spec.ratioMetric.total.dataSourceSpec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'sli' or 'sliRef' is set", + "'total' is set" + ] } - ] - }, - { - "path": "$.spec.sli.spec.ratioMetric.total.dataSourceSpec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." }, { "path": "$.spec.sli.spec.ratioMetric.total.spec", @@ -3829,6 +4319,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, + "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.total.spec.*~", "$.spec.sli.spec.ratioMetric.total.spec.*" @@ -3862,13 +4353,23 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'sli' or 'sliRef' is set", + "'raw' is set" + ] }, { "description": "must be one of: success, failure", - "errorCode": "one_of" + "errorCode": "one_of", + "conditions": [ + "'sli' or 'sliRef' is set", + "'raw' is set" + ] } - ] + ], + "typeDoc": "SLIRawMetricType identifies whether a raw ratio contains successes (good/total) or failures (bad/total).", + "fieldDoc": "RawType identifies whether Raw contains a success or failure ratio when Raw is used." }, { "path": "$.spec.sli.spec.ratioMetric.raw", @@ -3880,13 +4381,23 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set", + "'raw' is set" + ] }, { - "description": "properties are mutually exclusive: dataSourceRef, dataSourceSpec", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "'sli' or 'sliRef' is set", + "'raw' is set" + ] } ], + "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "fieldDoc": "Raw supplies an already computed ratio.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.raw.dataSourceRef", "$.spec.sli.spec.ratioMetric.raw.dataSourceSpec", @@ -3902,22 +4413,35 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set", + "'raw' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'sli' or 'sliRef' is set", + "'raw' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'sli' or 'sliRef' is set", + "'raw' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "DataSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#DataSource)." }, { "path": "$.spec.sli.spec.ratioMetric.raw.dataSourceSpec", @@ -3929,14 +4453,19 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set", + "'raw' is set" + ] } ], + "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", + "fieldDoc": "DataSourceSpec embeds the data-source configuration.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.raw.dataSourceSpec.description", "$.spec.sli.spec.ratioMetric.raw.dataSourceSpec.type", - "$.spec.sli.spec.ratioMetric.raw.dataSourceSpec.connectionDetails", - "$.spec.sli.spec.ratioMetric.raw.dataSourceSpec.connectionDetails[*]" + "$.spec.sli.spec.ratioMetric.raw.dataSourceSpec.connectionDetails" ] }, { @@ -3948,9 +4477,14 @@ "rules": [ { "description": "length must be less than or equal to 1050", - "errorCode": "string_max_length" + "errorCode": "string_max_length", + "conditions": [ + "'sli' or 'sliRef' is set", + "'raw' is set" + ] } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.sli.spec.ratioMetric.raw.dataSourceSpec.type", @@ -3961,30 +4495,33 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'sli' or 'sliRef' is set", + "'raw' is set" + ] } - ] + ], + "fieldDoc": "Type identifies the data-source implementation understood by the consumer." }, { "path": "$.spec.sli.spec.ratioMetric.raw.dataSourceSpec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'sli' or 'sliRef' is set", + "'raw' is set" + ] } - ] - }, - { - "path": "$.spec.sli.spec.ratioMetric.raw.dataSourceSpec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." }, { "path": "$.spec.sli.spec.ratioMetric.raw.spec", @@ -3992,6 +4529,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, + "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.raw.spec.*~", "$.spec.sli.spec.ratioMetric.raw.spec.*" @@ -4020,22 +4558,32 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'sli' or 'sliRef' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'sli' or 'sliRef' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "SLIRef names an existing [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLI) for a standard SLO." }, { "path": "$.spec.budgetingMethod", @@ -4058,7 +4606,9 @@ "description": "must be one of: Occurrences, Timeslices, RatioTimeslices", "errorCode": "one_of" } - ] + ], + "typeDoc": "SLOBudgetingMethod selects how an SLO consumes its error budget. Occurrences uses good events over total events, Timeslices uses good slices over total slices, and RatioTimeslices averages slice success ratios.", + "fieldDoc": "BudgetingMethod applies the selected error-budget calculation to every objective." }, { "path": "$.spec.timeWindow", @@ -4072,7 +4622,8 @@ "description": "length must be between 1 and 1", "errorCode": "slice_length" } - ] + ], + "fieldDoc": "TimeWindow defines the SLO evaluation period and its alignment." }, { "path": "$.spec.timeWindow[*]", @@ -4081,6 +4632,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "SLOTimeWindow describes one rolling or calendar-aligned SLO evaluation window.", "childrenPaths": [ "$.spec.timeWindow[*].duration", "$.spec.timeWindow[*].isRolling", @@ -4100,14 +4652,16 @@ "errorCode": "required" } ], - "typeDoc": "DurationShorthand is a shorthand representation of time duration. It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days)." + "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", + "fieldDoc": "Duration is the length of the evaluation window." }, { "path": "$.spec.timeWindow[*].isRolling", "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "IsRolling selects a rolling window when true and a calendar-aligned window when false." }, { "path": "$.spec.timeWindow[*].calendar", @@ -4122,6 +4676,8 @@ "errorCode": "optional" } ], + "typeDoc": "SLOCalendar defines the starting wall-clock time and time zone for a calendar-aligned [SLOTimeWindow](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOTimeWindow).", + "fieldDoc": "Calendar defines the alignment of a calendar window.", "childrenPaths": [ "$.spec.timeWindow[*].calendar.startTime", "$.spec.timeWindow[*].calendar.timeZone" @@ -4139,7 +4695,8 @@ "details": "date and time format follows Go's time layout, see https://pkg.go.dev/time#Layout for more details", "errorCode": "string_date_time" } - ] + ], + "fieldDoc": "StartTime is the local date and time when calendar alignment starts." }, { "path": "$.spec.timeWindow[*].calendar.timeZone", @@ -4157,7 +4714,8 @@ "Europe/Warsaw" ] } - ] + ], + "fieldDoc": "TimeZone determines how StartTime maps to an instant." }, { "path": "$.spec.objectives", @@ -4165,7 +4723,8 @@ "name": "[]SLOObjective", "kind": "[]struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" - } + }, + "fieldDoc": "Objectives contains the SLO's budget targets and metric thresholds. V2alpha permits multiple objectives for a threshold-metric SLO." }, { "path": "$.spec.objectives[*]", @@ -4176,17 +4735,19 @@ }, "rules": [ { - "description": "properties are mutually exclusive: target, targetPercent", + "description": "exactly one of 'target' and 'targetPercent' must be set", "errorCode": "mutually_exclusive" }, { - "description": "properties are mutually exclusive: sli, sliRef", + "description": "exactly one of 'sli' and 'sliRef' must be set", "errorCode": "mutually_exclusive", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] } ], + "typeDoc": "SLOObjective defines one error-budget target and, for a threshold SLI, its metric comparison. The SDK validates threshold-specific fields only for an embedded SLI. A referenced SLI does not expose its metric type during validation.", "childrenPaths": [ "$.spec.objectives[*].displayName", "$.spec.objectives[*].op", @@ -4205,7 +4766,8 @@ "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "DisplayName is a human-readable name for this objective. It is not part of the enclosing object's [Metadata](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#Metadata)." }, { "path": "$.spec.objectives[*].op", @@ -4242,7 +4804,9 @@ "'sli.spec.thresholdMetric' is set" ] } - ] + ], + "typeDoc": "Operator specifies a comparison operation for an SLO objective or alert condition.", + "fieldDoc": "Operator compares a threshold metric with Value." }, { "path": "$.spec.objectives[*].value", @@ -4265,7 +4829,8 @@ "'sli.spec.thresholdMetric' is set" ] } - ] + ], + "fieldDoc": "Value is the comparison threshold for a threshold metric." }, { "path": "$.spec.objectives[*].target", @@ -4286,7 +4851,8 @@ "description": "must be less than '1'", "errorCode": "less_than" } - ] + ], + "fieldDoc": "Target is the desired success proportion. For example, \"0.995\" means 99.5 percent." }, { "path": "$.spec.objectives[*].targetPercent", @@ -4307,7 +4873,8 @@ "description": "must be less than '100'", "errorCode": "less_than" } - ] + ], + "fieldDoc": "TargetPercent is the desired success percentage." }, { "path": "$.spec.objectives[*].timeSliceTarget", @@ -4318,17 +4885,27 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'budgetingMethod' is 'Timeslices'" + ] }, { "description": "must be greater than '0'", - "errorCode": "greater_than" + "errorCode": "greater_than", + "conditions": [ + "'budgetingMethod' is 'Timeslices'" + ] }, { "description": "must be less than or equal to '1'", - "errorCode": "less_than_or_equal_to" + "errorCode": "less_than_or_equal_to", + "conditions": [ + "'budgetingMethod' is 'Timeslices'" + ] } - ] + ], + "fieldDoc": "TimeSliceTarget sets the per-slice success threshold for Timeslices." }, { "path": "$.spec.objectives[*].timeSliceWindow", @@ -4340,10 +4917,21 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'budgetingMethod' is 'Timeslices'" + ] + }, + { + "description": "property is required", + "errorCode": "required", + "conditions": [ + "'budgetingMethod' is 'RatioTimeslices'" + ] } ], - "typeDoc": "DurationShorthand is a shorthand representation of time duration. It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days)." + "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", + "fieldDoc": "TimeSliceWindow sets the size of each slice for Timeslices and RatioTimeslices. OpenSLO also accepts a number interpreted as minutes. This SDK represents only duration shorthand." }, { "path": "$.spec.objectives[*].sli", @@ -4357,10 +4945,13 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] } ], + "typeDoc": "SLOSLIInline embeds an SLI definition in an SLO or one of its objectives.", + "fieldDoc": "SLI embeds this objective's service level indicator for a composite SLO.", "childrenPaths": [ "$.spec.objectives[*].sli.metadata", "$.spec.objectives[*].sli.spec" @@ -4378,10 +4969,12 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] } ], + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", "childrenPaths": [ "$.spec.objectives[*].sli.metadata.name", "$.spec.objectives[*].sli.metadata.labels", @@ -4399,14 +4992,16 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] }, { "description": "length must be between 1 and 63", "errorCode": "string_dns_label:string_length", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] }, { @@ -4414,14 +5009,16 @@ "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object when other OpenSLO objects refer to it." }, { "path": "$.spec.objectives[*].sli.metadata.labels", @@ -4430,6 +5027,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Labels maps label keys to one string value each.", + "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.spec.objectives[*].sli.metadata.labels.*~", "$.spec.objectives[*].sli.metadata.labels.*" @@ -4446,7 +5045,8 @@ "description": "length must be between 1 and 317", "errorCode": "string_kubernetes_qualified_name:string_length", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] }, { @@ -4454,7 +5054,8 @@ "details": "Kubernetes Qualified Name must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character with an optional DNS subdomain prefix and '/'", "errorCode": "string_kubernetes_qualified_name", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ], "examples": [ "my.domain/MyName", @@ -4476,7 +5077,8 @@ "description": "string must match regular expression: '^([a-z0-9]([-._a-z0-9]{0,61}[a-z0-9])?)?$'", "errorCode": "string_match_regexp", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ], "examples": [ "my-label", @@ -4492,6 +5094,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Annotations maps annotation keys to arbitrary string values.", + "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.spec.objectives[*].sli.metadata.annotations.*~", "$.spec.objectives[*].sli.metadata.annotations.*" @@ -4508,7 +5112,8 @@ "description": "length must be between 1 and 317", "errorCode": "string_kubernetes_qualified_name:string_length", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] }, { @@ -4516,7 +5121,8 @@ "details": "Kubernetes Qualified Name must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character with an optional DNS subdomain prefix and '/'", "errorCode": "string_kubernetes_qualified_name", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ], "examples": [ "my.domain/MyName", @@ -4543,13 +5149,15 @@ }, "rules": [ { - "description": "properties are mutually exclusive: ratioMetric, thresholdMetric", + "description": "exactly one of 'thresholdMetric' and 'ratioMetric' must be set", "errorCode": "mutually_exclusive", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] } ], + "typeDoc": "SLISpec defines the metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLI).", "childrenPaths": [ "$.spec.objectives[*].sli.spec.description", "$.spec.objectives[*].sli.spec.thresholdMetric", @@ -4567,10 +5175,12 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] } - ] + ], + "fieldDoc": "Description summarizes the indicator." }, { "path": "$.spec.objectives[*].sli.spec.thresholdMetric", @@ -4584,17 +5194,21 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] }, { - "description": "properties are mutually exclusive: dataSourceRef, dataSourceSpec", + "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", "errorCode": "mutually_exclusive", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] } ], + "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "fieldDoc": "ThresholdMetric supplies values for [SLOObjective.Operator](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Operator) to compare with [SLOObjective.Value](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Value).", "childrenPaths": [ "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceRef", "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceSpec", @@ -4612,14 +5226,16 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] }, { "description": "length must be between 1 and 63", "errorCode": "string_dns_label:string_length", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] }, { @@ -4627,14 +5243,16 @@ "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "DataSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#DataSource)." }, { "path": "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceSpec", @@ -4648,15 +5266,17 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] } ], + "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", + "fieldDoc": "DataSourceSpec embeds the data-source configuration.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceSpec.description", "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceSpec.type", - "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceSpec.connectionDetails", - "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceSpec.connectionDetails[*]" + "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceSpec.connectionDetails" ] }, { @@ -4670,10 +5290,12 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceSpec.type", @@ -4686,16 +5308,18 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] } - ] + ], + "fieldDoc": "Type identifies the data-source implementation understood by the consumer." }, { "path": "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceSpec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ @@ -4703,17 +5327,12 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] } - ] - }, - { - "path": "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceSpec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." }, { "path": "$.spec.objectives[*].sli.spec.thresholdMetric.spec", @@ -4721,6 +5340,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, + "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.thresholdMetric.spec.*~", "$.spec.objectives[*].sli.spec.thresholdMetric.spec.*" @@ -4752,31 +5372,38 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] }, { - "description": "properties are mutually exclusive: raw, total", + "description": "exactly one of 'total' and 'raw' must be set", "errorCode": "mutually_exclusive", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] }, { "description": "properties are mutually exclusive: bad, good, raw", "errorCode": "mutually_exclusive", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] }, { "description": "one of [bad, good] properties must be set, none was provided", "errorCode": "one_of_properties", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set" ] } ], + "typeDoc": "SLIRatioMetric derives a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 990 good events out of 1,000 total events produce \"0.99\". 10 bad events with the same total produce the same success ratio.", + "fieldDoc": "RatioMetric supplies a success ratio for an SLO objective.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.counter", "$.spec.objectives[*].sli.spec.ratioMetric.good", @@ -4791,7 +5418,8 @@ "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "Counter reports whether the good, bad, and total metrics are monotonically increasing counters. It has no effect when Raw is used." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.good", @@ -4805,17 +5433,25 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ] }, { - "description": "properties are mutually exclusive: dataSourceRef, dataSourceSpec", + "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", "errorCode": "mutually_exclusive", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ] } ], + "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "fieldDoc": "Good is the success-count numerator used with Total.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceRef", "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceSpec", @@ -4833,14 +5469,20 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ] }, { "description": "length must be between 1 and 63", "errorCode": "string_dns_label:string_length", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ] }, { @@ -4848,14 +5490,18 @@ "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "DataSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#DataSource)." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceSpec", @@ -4869,15 +5515,19 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ] } ], + "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", + "fieldDoc": "DataSourceSpec embeds the data-source configuration.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceSpec.description", "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceSpec.type", - "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceSpec.connectionDetails", - "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceSpec.connectionDetails[*]" + "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceSpec.connectionDetails" ] }, { @@ -4891,10 +5541,14 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ] } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceSpec.type", @@ -4907,16 +5561,20 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ] } - ] + ], + "fieldDoc": "Type identifies the data-source implementation understood by the consumer." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceSpec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ @@ -4924,17 +5582,14 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ] } - ] - }, - { - "path": "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceSpec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.good.spec", @@ -4942,6 +5597,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, + "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.good.spec.*~", "$.spec.objectives[*].sli.spec.ratioMetric.good.spec.*" @@ -4973,17 +5629,25 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ] }, { - "description": "properties are mutually exclusive: dataSourceRef, dataSourceSpec", + "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", "errorCode": "mutually_exclusive", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ] } ], + "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "fieldDoc": "Bad is the failure-count input used with Total to derive successes.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceRef", "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceSpec", @@ -5001,14 +5665,20 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ] }, { "description": "length must be between 1 and 63", "errorCode": "string_dns_label:string_length", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ] }, { @@ -5016,14 +5686,18 @@ "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "DataSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#DataSource)." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceSpec", @@ -5037,15 +5711,19 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ] } ], + "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", + "fieldDoc": "DataSourceSpec embeds the data-source configuration.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceSpec.description", "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceSpec.type", - "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceSpec.connectionDetails", - "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceSpec.connectionDetails[*]" + "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceSpec.connectionDetails" ] }, { @@ -5059,10 +5737,14 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ] } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceSpec.type", @@ -5075,16 +5757,20 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ] } - ] + ], + "fieldDoc": "Type identifies the data-source implementation understood by the consumer." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceSpec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ @@ -5092,17 +5778,14 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set", + "'bad' is set" ] } - ] - }, - { - "path": "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceSpec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.bad.spec", @@ -5110,6 +5793,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, + "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.bad.spec.*~", "$.spec.objectives[*].sli.spec.ratioMetric.bad.spec.*" @@ -5141,17 +5825,23 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set" ] }, { - "description": "properties are mutually exclusive: dataSourceRef, dataSourceSpec", + "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", "errorCode": "mutually_exclusive", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set" ] } ], + "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "fieldDoc": "Total is the denominator paired with Good or Bad.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceRef", "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceSpec", @@ -5169,14 +5859,18 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set" ] }, { "description": "length must be between 1 and 63", "errorCode": "string_dns_label:string_length", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set" ] }, { @@ -5184,14 +5878,17 @@ "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set" ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "DataSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#DataSource)." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceSpec", @@ -5205,15 +5902,18 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set" ] } ], + "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", + "fieldDoc": "DataSourceSpec embeds the data-source configuration.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceSpec.description", "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceSpec.type", - "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceSpec.connectionDetails", - "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceSpec.connectionDetails[*]" + "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceSpec.connectionDetails" ] }, { @@ -5227,10 +5927,13 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set" ] } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceSpec.type", @@ -5243,16 +5946,19 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set" ] } - ] + ], + "fieldDoc": "Type identifies the data-source implementation understood by the consumer." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceSpec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ @@ -5260,17 +5966,13 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'total' is set" ] } - ] - }, - { - "path": "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceSpec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.total.spec", @@ -5278,6 +5980,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, + "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.total.spec.*~", "$.spec.objectives[*].sli.spec.ratioMetric.total.spec.*" @@ -5313,17 +6016,23 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'raw' is set" ] }, { "description": "must be one of: success, failure", "errorCode": "one_of", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'raw' is set" ] } - ] + ], + "typeDoc": "SLIRawMetricType identifies whether a raw ratio contains successes (good/total) or failures (bad/total).", + "fieldDoc": "RawType identifies whether Raw contains a success or failure ratio when Raw is used." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.raw", @@ -5337,17 +6046,23 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'raw' is set" ] }, { - "description": "properties are mutually exclusive: dataSourceRef, dataSourceSpec", + "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", "errorCode": "mutually_exclusive", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'raw' is set" ] } ], + "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "fieldDoc": "Raw supplies an already computed ratio.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceRef", "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceSpec", @@ -5365,14 +6080,18 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'raw' is set" ] }, { "description": "length must be between 1 and 63", "errorCode": "string_dns_label:string_length", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'raw' is set" ] }, { @@ -5380,14 +6099,17 @@ "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'raw' is set" ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "DataSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#DataSource)." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceSpec", @@ -5401,15 +6123,18 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'raw' is set" ] } ], + "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", + "fieldDoc": "DataSourceSpec embeds the data-source configuration.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceSpec.description", "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceSpec.type", - "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceSpec.connectionDetails", - "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceSpec.connectionDetails[*]" + "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceSpec.connectionDetails" ] }, { @@ -5423,10 +6148,13 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'raw' is set" ] } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceSpec.type", @@ -5439,16 +6167,19 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'raw' is set" ] } - ] + ], + "fieldDoc": "Type identifies the data-source implementation understood by the consumer." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceSpec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ @@ -5456,17 +6187,13 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set", + "'raw' is set" ] } - ] - }, - { - "path": "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceSpec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.raw.spec", @@ -5474,6 +6201,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, + "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.raw.spec.*~", "$.spec.objectives[*].sli.spec.ratioMetric.raw.spec.*" @@ -5504,14 +6232,16 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] }, { "description": "length must be between 1 and 63", "errorCode": "string_dns_label:string_length", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ] }, { @@ -5519,14 +6249,16 @@ "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", "conditions": [ - "is composite SLO" + "is composite SLO", + "'sli' or 'sliRef' is set" ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "SLIRef names this objective's existing [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLI) for a composite SLO." }, { "path": "$.spec.objectives[*].compositeWeight", @@ -5549,7 +6281,8 @@ "is composite SLO" ] } - ] + ], + "fieldDoc": "CompositeWeight scales this objective's contribution to a multi-objective composite SLO. OpenSLO uses a weight of 1 when omitted. This SDK leaves the field unset." }, { "path": "$.spec.alertPolicies", @@ -5557,7 +6290,8 @@ "name": "[]SLOAlertPolicy", "kind": "[]struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" - } + }, + "fieldDoc": "AlertPolicies contains inline or referenced policies associated with the SLO." }, { "path": "$.spec.alertPolicies[*]", @@ -5568,7 +6302,7 @@ }, "rules": [ { - "description": "properties are mutually exclusive: spec, targetRef", + "description": "exactly one of 'alertPolicyRef' and 'spec' must be set", "errorCode": "mutually_exclusive" }, { @@ -5576,6 +6310,7 @@ "errorCode": "optional" } ], + "typeDoc": "SLOAlertPolicy associates an alert policy with an [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLO).", "childrenPaths": [ "$.spec.alertPolicies[*].kind", "$.spec.alertPolicies[*].metadata", @@ -5618,6 +6353,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", "childrenPaths": [ "$.spec.alertPolicies[*].metadata.name", "$.spec.alertPolicies[*].metadata.labels", @@ -5648,7 +6384,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object when other OpenSLO objects refer to it." }, { "path": "$.spec.alertPolicies[*].metadata.labels", @@ -5657,6 +6394,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Labels maps label keys to one string value each.", + "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.spec.alertPolicies[*].metadata.labels.*~", "$.spec.alertPolicies[*].metadata.labels.*" @@ -5710,6 +6449,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Annotations maps annotation keys to arbitrary string values.", + "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.spec.alertPolicies[*].metadata.annotations.*~", "$.spec.alertPolicies[*].metadata.annotations.*" @@ -5759,6 +6500,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertPolicySpec defines the trigger states, condition, and notification destinations for an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy).", "childrenPaths": [ "$.spec.alertPolicies[*].spec.description", "$.spec.alertPolicies[*].spec.alertWhenNoData", @@ -5781,28 +6523,32 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the alert policy." }, { "path": "$.spec.alertPolicies[*].spec.alertWhenNoData", "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "AlertWhenNoData enables notifications when the associated SLO has no burn-rate value." }, { "path": "$.spec.alertPolicies[*].spec.alertWhenBreaching", "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "AlertWhenBreaching enables notifications when the condition starts breaching." }, { "path": "$.spec.alertPolicies[*].spec.alertWhenResolved", "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "AlertWhenResolved enables notifications when the condition resolves." }, { "path": "$.spec.alertPolicies[*].spec.conditions", @@ -5816,7 +6562,8 @@ "description": "length must be between 1 and 1", "errorCode": "slice_length" } - ] + ], + "fieldDoc": "Conditions supplies the policy's alert condition by reference or inline." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*]", @@ -5827,7 +6574,7 @@ }, "rules": [ { - "description": "properties are mutually exclusive: conditionRef, spec", + "description": "exactly one of 'conditionRef' and 'spec' must be set", "errorCode": "mutually_exclusive" }, { @@ -5835,6 +6582,7 @@ "errorCode": "optional" } ], + "typeDoc": "AlertPolicyCondition supplies a condition to an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy) by reference or inline definition.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].conditionRef", "$.spec.alertPolicies[*].spec.conditions[*].kind", @@ -5866,7 +6614,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "ConditionRef names the alert condition to use." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].kind", @@ -5903,6 +6652,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].metadata.name", "$.spec.alertPolicies[*].spec.conditions[*].metadata.labels", @@ -5933,7 +6683,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object when other OpenSLO objects refer to it." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].metadata.labels", @@ -5942,6 +6693,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Labels maps label keys to one string value each.", + "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].metadata.labels.*~", "$.spec.alertPolicies[*].spec.conditions[*].metadata.labels.*" @@ -5995,6 +6748,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Annotations maps annotation keys to arbitrary string values.", + "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].metadata.annotations.*~", "$.spec.alertPolicies[*].spec.conditions[*].metadata.annotations.*" @@ -6044,6 +6799,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertConditionSpec defines an alert's severity and breach condition.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].spec.severity", "$.spec.alertPolicies[*].spec.conditions[*].spec.condition", @@ -6061,7 +6817,8 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Severity is a consumer-defined alert classification." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.condition", @@ -6076,6 +6833,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertConditionType defines a burn-rate comparison over a lookback window. Burn rate is error-budget consumption relative to the rate allowed by the SLO.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].spec.condition.kind", "$.spec.alertPolicies[*].spec.conditions[*].spec.condition.op", @@ -6103,7 +6861,9 @@ "description": "must be one of: burnrate", "errorCode": "one_of" } - ] + ], + "typeDoc": "AlertConditionKind identifies the evaluation algorithm for an [AlertConditionType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionType).", + "fieldDoc": "Kind selects the condition algorithm. OpenSLO defaults omitted values to [AlertConditionKindBurnRate](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionKindBurnRate). This SDK does not apply that default." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.condition.op", @@ -6121,13 +6881,21 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] }, { "description": "must be one of: gt, lt, gte, lte", - "errorCode": "one_of" + "errorCode": "one_of", + "conditions": [ + "'kind' is 'burnrate'" + ] } - ] + ], + "typeDoc": "Operator specifies a comparison operation for an SLO objective or alert condition.", + "fieldDoc": "Operator compares the calculated burn rate with Threshold." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.condition.threshold", @@ -6138,9 +6906,13 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] } - ] + ], + "fieldDoc": "Threshold sets the numeric burn-rate boundary." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.condition.lookbackWindow", @@ -6152,10 +6924,14 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] } ], - "typeDoc": "DurationShorthand is a shorthand representation of time duration. It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days)." + "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", + "fieldDoc": "LookbackWindow sets the period for burn-rate calculation." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.condition.alertAfter", @@ -6167,10 +6943,14 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] } ], - "typeDoc": "DurationShorthand is a shorthand representation of time duration. It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days)." + "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", + "fieldDoc": "AlertAfter sets how long the comparison must hold before the condition breaches. OpenSLO defaults omitted values to \"0m\". This SDK does not apply that default." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.description", @@ -6183,7 +6963,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the alert condition." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets", @@ -6197,7 +6978,8 @@ "description": "length must be greater than or equal to 1", "errorCode": "slice_min_length" } - ] + ], + "fieldDoc": "NotificationTargets lists referenced or inline delivery destinations." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets[*]", @@ -6208,7 +6990,7 @@ }, "rules": [ { - "description": "properties are mutually exclusive: spec, targetRef", + "description": "exactly one of 'targetRef' and 'spec' must be set", "errorCode": "mutually_exclusive" }, { @@ -6216,6 +6998,7 @@ "errorCode": "optional" } ], + "typeDoc": "AlertPolicyNotificationTarget supplies a notification destination to an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy) by reference or inline definition.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].targetRef", "$.spec.alertPolicies[*].spec.notificationTargets[*].kind", @@ -6247,7 +7030,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "TargetRef names the notification target to use." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets[*].kind", @@ -6284,6 +7068,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.name", "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.labels", @@ -6314,7 +7099,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object when other OpenSLO objects refer to it." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.labels", @@ -6323,6 +7109,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Labels maps label keys to one string value each.", + "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.labels.*~", "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.labels.*" @@ -6376,6 +7164,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Annotations maps annotation keys to arbitrary string values.", + "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.annotations.*~", "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.annotations.*" @@ -6425,6 +7215,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertNotificationTargetSpec identifies the consumer-defined notification destination.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].spec.description", "$.spec.alertPolicies[*].spec.notificationTargets[*].spec.target" @@ -6441,7 +7232,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the destination." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets[*].spec.target", @@ -6454,7 +7246,8 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Target identifies the consumer-defined destination for alert delivery." }, { "path": "$.spec.alertPolicies[*].alertPolicyRef", @@ -6480,7 +7273,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "AlertPolicyRef names the alert policy to use." } ] }, @@ -6494,6 +7288,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Service groups SLOs under a named service.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -6558,6 +7353,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", "childrenPaths": [ "$.metadata.name", "$.metadata.labels", @@ -6588,7 +7384,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object when other OpenSLO objects refer to it." }, { "path": "$.metadata.labels", @@ -6597,6 +7394,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Labels maps label keys to one string value each.", + "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*" @@ -6650,6 +7449,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "Annotations maps annotation keys to arbitrary string values.", + "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -6693,6 +7494,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "typeDoc": "ServiceSpec defines the descriptive attributes of a [Service](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#Service).", "childrenPaths": [ "$.spec.description" ] @@ -6708,7 +7510,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the service." } ] } @@ -6724,6 +7527,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "AlertCondition defines the condition under which an SLO alert is considered breaching.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -6788,6 +7592,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata identifies and describes an OpenSLO v1 object.", "childrenPaths": [ "$.metadata.name", "$.metadata.displayName", @@ -6819,7 +7624,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object." }, { "path": "$.metadata.displayName", @@ -6836,7 +7642,8 @@ "description": "length must be less than or equal to 63", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "DisplayName is the object's human-readable name." }, { "path": "$.metadata.labels", @@ -6845,6 +7652,8 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Labels maps metadata keys to one or more values.", + "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*", @@ -6886,6 +7695,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", + "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -6932,6 +7743,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertConditionSpec defines an alert's severity and burn-rate condition.", "childrenPaths": [ "$.spec.severity", "$.spec.condition", @@ -6949,7 +7761,8 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Severity is an implementation-defined classification such as \"sev1\" or \"page\"." }, { "path": "$.spec.condition", @@ -6964,6 +7777,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertConditionType defines a comparison against an SLO's burn rate. Burn rate is error-budget consumption relative to the rate allowed by the SLO.", "childrenPaths": [ "$.spec.condition.kind", "$.spec.condition.op", @@ -6991,7 +7805,9 @@ "description": "must be one of: burnrate", "errorCode": "one_of" } - ] + ], + "typeDoc": "AlertConditionKind identifies the calculation used by an [AlertConditionType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertConditionType).", + "fieldDoc": "Kind selects the condition calculation. OpenSLO defaults Kind to [AlertConditionKindBurnRate](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertConditionKindBurnRate). This SDK does not apply that default." }, { "path": "$.spec.condition.op", @@ -7009,13 +7825,21 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] }, { "description": "must be one of: gt, lt, gte, lte", - "errorCode": "one_of" + "errorCode": "one_of", + "conditions": [ + "'kind' is 'burnrate'" + ] } - ] + ], + "typeDoc": "Operator identifies a comparison applied to a metric value.", + "fieldDoc": "Operator compares the calculated burn rate with Threshold." }, { "path": "$.spec.condition.threshold", @@ -7026,9 +7850,13 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] } - ] + ], + "fieldDoc": "Threshold sets the numeric burn-rate boundary." }, { "path": "$.spec.condition.lookbackWindow", @@ -7040,10 +7868,14 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] } ], - "typeDoc": "DurationShorthand is a shorthand representation of time duration. It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days)." + "typeDoc": "DurationShorthand represents a duration as an integer and a case-sensitive [DurationShorthandUnit](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DurationShorthandUnit), such as \"1m\" or \"10d\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", + "fieldDoc": "LookbackWindow sets the period for burn-rate calculation." }, { "path": "$.spec.condition.alertAfter", @@ -7055,10 +7887,14 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'kind' is 'burnrate'" + ] } ], - "typeDoc": "DurationShorthand is a shorthand representation of time duration. It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days)." + "typeDoc": "DurationShorthand represents a duration as an integer and a case-sensitive [DurationShorthandUnit](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DurationShorthandUnit), such as \"1m\" or \"10d\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", + "fieldDoc": "AlertAfter sets how long the comparison must hold before the condition breaches. OpenSLO treats an omitted value as \"0m\". This SDK leaves it unset." }, { "path": "$.spec.description", @@ -7071,7 +7907,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the alert condition." } ] }, @@ -7085,6 +7922,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "AlertNotificationTarget identifies a destination for SLO alert notifications.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -7149,6 +7987,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata identifies and describes an OpenSLO v1 object.", "childrenPaths": [ "$.metadata.name", "$.metadata.displayName", @@ -7180,7 +8019,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object." }, { "path": "$.metadata.displayName", @@ -7197,7 +8037,8 @@ "description": "length must be less than or equal to 63", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "DisplayName is the object's human-readable name." }, { "path": "$.metadata.labels", @@ -7206,6 +8047,8 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Labels maps metadata keys to one or more values.", + "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*", @@ -7247,6 +8090,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", + "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -7287,6 +8132,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "AlertNotificationTargetSpec defines an implementation-specific notification destination.", "childrenPaths": [ "$.spec.description", "$.spec.target" @@ -7303,7 +8149,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the destination." }, { "path": "$.spec.target", @@ -7316,7 +8163,8 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Target identifies the implementation-defined delivery target." } ] }, @@ -7330,6 +8178,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "AlertPolicy defines when the system emits alerts for an SLO and where it sends them. It is distinct from an organization's policy for responding to error-budget consumption.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -7394,6 +8243,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata identifies and describes an OpenSLO v1 object.", "childrenPaths": [ "$.metadata.name", "$.metadata.displayName", @@ -7425,7 +8275,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object." }, { "path": "$.metadata.displayName", @@ -7442,7 +8293,8 @@ "description": "length must be less than or equal to 63", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "DisplayName is the object's human-readable name." }, { "path": "$.metadata.labels", @@ -7451,6 +8303,8 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Labels maps metadata keys to one or more values.", + "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*", @@ -7492,6 +8346,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", + "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -7532,6 +8388,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "AlertPolicySpec defines the events that emit notifications and the condition and destinations used by an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicy).", "childrenPaths": [ "$.spec.description", "$.spec.alertWhenNoData", @@ -7554,28 +8411,32 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the alert policy." }, { "path": "$.spec.alertWhenNoData", "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "AlertWhenNoData enables alerts when the associated SLO has no burn-rate value." }, { "path": "$.spec.alertWhenBreaching", "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "AlertWhenBreaching enables alerts when the condition starts breaching." }, { "path": "$.spec.alertWhenResolved", "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "AlertWhenResolved enables alerts when the condition resolves." }, { "path": "$.spec.conditions", @@ -7589,7 +8450,8 @@ "description": "length must be between 1 and 1", "errorCode": "slice_length" } - ] + ], + "fieldDoc": "Conditions contains the alert condition, inline or by reference." }, { "path": "$.spec.conditions[*]", @@ -7600,7 +8462,7 @@ }, "rules": [ { - "description": "properties are mutually exclusive: conditionRef, spec", + "description": "exactly one of 'conditionRef' and 'spec' must be set", "errorCode": "mutually_exclusive" }, { @@ -7608,6 +8470,7 @@ "errorCode": "optional" } ], + "typeDoc": "AlertPolicyCondition supplies an alert condition to an [AlertPolicySpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicySpec) by reference or inline definition.", "childrenPaths": [ "$.spec.conditions[*].conditionRef", "$.spec.conditions[*].kind", @@ -7639,7 +8502,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "ConditionRef names an existing alert condition." }, { "path": "$.spec.conditions[*].kind", @@ -7676,6 +8540,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata identifies and describes an OpenSLO v1 object.", "childrenPaths": [ "$.spec.conditions[*].metadata.name", "$.spec.conditions[*].metadata.displayName", @@ -7707,7 +8572,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object." }, { "path": "$.spec.conditions[*].metadata.displayName", @@ -7724,7 +8590,8 @@ "description": "length must be less than or equal to 63", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "DisplayName is the object's human-readable name." }, { "path": "$.spec.conditions[*].metadata.labels", @@ -7733,6 +8600,8 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Labels maps metadata keys to one or more values.", + "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", "childrenPaths": [ "$.spec.conditions[*].metadata.labels.*~", "$.spec.conditions[*].metadata.labels.*", @@ -7774,6 +8643,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", + "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ "$.spec.conditions[*].metadata.annotations.*~", "$.spec.conditions[*].metadata.annotations.*" @@ -7820,6 +8691,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertConditionSpec defines an alert's severity and burn-rate condition.", "childrenPaths": [ "$.spec.conditions[*].spec.severity", "$.spec.conditions[*].spec.condition", @@ -7837,7 +8709,8 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Severity is an implementation-defined classification such as \"sev1\" or \"page\"." }, { "path": "$.spec.conditions[*].spec.condition", @@ -7852,6 +8725,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertConditionType defines a comparison against an SLO's burn rate. Burn rate is error-budget consumption relative to the rate allowed by the SLO.", "childrenPaths": [ "$.spec.conditions[*].spec.condition.kind", "$.spec.conditions[*].spec.condition.op", @@ -7879,7 +8753,9 @@ "description": "must be one of: burnrate", "errorCode": "one_of" } - ] + ], + "typeDoc": "AlertConditionKind identifies the calculation used by an [AlertConditionType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertConditionType).", + "fieldDoc": "Kind selects the condition calculation. OpenSLO defaults Kind to [AlertConditionKindBurnRate](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertConditionKindBurnRate). This SDK does not apply that default." }, { "path": "$.spec.conditions[*].spec.condition.op", @@ -7897,13 +8773,21 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] }, { "description": "must be one of: gt, lt, gte, lte", - "errorCode": "one_of" + "errorCode": "one_of", + "conditions": [ + "'kind' is 'burnrate'" + ] } - ] + ], + "typeDoc": "Operator identifies a comparison applied to a metric value.", + "fieldDoc": "Operator compares the calculated burn rate with Threshold." }, { "path": "$.spec.conditions[*].spec.condition.threshold", @@ -7914,9 +8798,13 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] } - ] + ], + "fieldDoc": "Threshold sets the numeric burn-rate boundary." }, { "path": "$.spec.conditions[*].spec.condition.lookbackWindow", @@ -7928,10 +8816,14 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] } ], - "typeDoc": "DurationShorthand is a shorthand representation of time duration. It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days)." + "typeDoc": "DurationShorthand represents a duration as an integer and a case-sensitive [DurationShorthandUnit](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DurationShorthandUnit), such as \"1m\" or \"10d\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", + "fieldDoc": "LookbackWindow sets the period for burn-rate calculation." }, { "path": "$.spec.conditions[*].spec.condition.alertAfter", @@ -7943,10 +8835,14 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'kind' is 'burnrate'" + ] } ], - "typeDoc": "DurationShorthand is a shorthand representation of time duration. It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days)." + "typeDoc": "DurationShorthand represents a duration as an integer and a case-sensitive [DurationShorthandUnit](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DurationShorthandUnit), such as \"1m\" or \"10d\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", + "fieldDoc": "AlertAfter sets how long the comparison must hold before the condition breaches. OpenSLO treats an omitted value as \"0m\". This SDK leaves it unset." }, { "path": "$.spec.conditions[*].spec.description", @@ -7959,7 +8855,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the alert condition." }, { "path": "$.spec.notificationTargets", @@ -7973,7 +8870,8 @@ "description": "length must be greater than or equal to 1", "errorCode": "slice_min_length" } - ] + ], + "fieldDoc": "NotificationTargets lists notification destinations, inline or by reference." }, { "path": "$.spec.notificationTargets[*]", @@ -7984,7 +8882,7 @@ }, "rules": [ { - "description": "properties are mutually exclusive: spec, targetRef", + "description": "exactly one of 'targetRef' and 'spec' must be set", "errorCode": "mutually_exclusive" }, { @@ -7992,6 +8890,7 @@ "errorCode": "optional" } ], + "typeDoc": "AlertPolicyNotificationTarget supplies a notification target to an [AlertPolicySpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicySpec) by reference or inline definition.", "childrenPaths": [ "$.spec.notificationTargets[*].targetRef", "$.spec.notificationTargets[*].kind", @@ -8023,7 +8922,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "TargetRef names an existing notification target." }, { "path": "$.spec.notificationTargets[*].kind", @@ -8060,6 +8960,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata identifies and describes an OpenSLO v1 object.", "childrenPaths": [ "$.spec.notificationTargets[*].metadata.name", "$.spec.notificationTargets[*].metadata.displayName", @@ -8091,7 +8992,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object." }, { "path": "$.spec.notificationTargets[*].metadata.displayName", @@ -8108,7 +9010,8 @@ "description": "length must be less than or equal to 63", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "DisplayName is the object's human-readable name." }, { "path": "$.spec.notificationTargets[*].metadata.labels", @@ -8117,6 +9020,8 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Labels maps metadata keys to one or more values.", + "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", "childrenPaths": [ "$.spec.notificationTargets[*].metadata.labels.*~", "$.spec.notificationTargets[*].metadata.labels.*", @@ -8158,6 +9063,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", + "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ "$.spec.notificationTargets[*].metadata.annotations.*~", "$.spec.notificationTargets[*].metadata.annotations.*" @@ -8204,6 +9111,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertNotificationTargetSpec defines an implementation-specific notification destination.", "childrenPaths": [ "$.spec.notificationTargets[*].spec.description", "$.spec.notificationTargets[*].spec.target" @@ -8220,7 +9128,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the destination." }, { "path": "$.spec.notificationTargets[*].spec.target", @@ -8233,7 +9142,8 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Target identifies the implementation-defined delivery target." } ] }, @@ -8247,6 +9157,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "DataSource stores reusable connection details for a metrics backend.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -8311,6 +9222,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata identifies and describes an OpenSLO v1 object.", "childrenPaths": [ "$.metadata.name", "$.metadata.displayName", @@ -8342,7 +9254,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object." }, { "path": "$.metadata.displayName", @@ -8359,7 +9272,8 @@ "description": "length must be less than or equal to 63", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "DisplayName is the object's human-readable name." }, { "path": "$.metadata.labels", @@ -8368,6 +9282,8 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Labels maps metadata keys to one or more values.", + "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*", @@ -8409,6 +9325,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", + "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -8449,11 +9367,11 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "DataSourceSpec defines reusable, source-specific connection configuration.", "childrenPaths": [ "$.spec.description", "$.spec.type", - "$.spec.connectionDetails", - "$.spec.connectionDetails[*]" + "$.spec.connectionDetails" ] }, { @@ -8467,7 +9385,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the data source." }, { "path": "$.spec.type", @@ -8480,13 +9399,14 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Type names the implementation-defined metric source." }, { "path": "$.spec.connectionDetails", "typeInfo": { "name": "RawMessage", - "kind": "[]uint8", + "kind": "JSON", "package": "encoding/json" }, "rules": [ @@ -8494,14 +9414,8 @@ "description": "property is required", "errorCode": "required" } - ] - }, - { - "path": "$.spec.connectionDetails[*]", - "typeInfo": { - "name": "uint8", - "kind": "uint8" - } + ], + "fieldDoc": "ConnectionDetails stores source-specific connection configuration as JSON." } ] }, @@ -8515,6 +9429,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "SLI describes how to read metric data used to evaluate an [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO).", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -8579,6 +9494,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata identifies and describes an OpenSLO v1 object.", "childrenPaths": [ "$.metadata.name", "$.metadata.displayName", @@ -8610,7 +9526,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object." }, { "path": "$.metadata.displayName", @@ -8627,7 +9544,8 @@ "description": "length must be less than or equal to 63", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "DisplayName is the object's human-readable name." }, { "path": "$.metadata.labels", @@ -8636,6 +9554,8 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Labels maps metadata keys to one or more values.", + "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*", @@ -8677,6 +9597,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", + "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -8719,10 +9641,11 @@ }, "rules": [ { - "description": "properties are mutually exclusive: ratioMetric, thresholdMetric", + "description": "exactly one of 'thresholdMetric' and 'ratioMetric' must be set", "errorCode": "mutually_exclusive" } ], + "typeDoc": "SLISpec defines the metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", "childrenPaths": [ "$.spec.description", "$.spec.thresholdMetric", @@ -8740,7 +9663,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the SLI." }, { "path": "$.spec.thresholdMetric", @@ -8755,6 +9679,8 @@ "errorCode": "optional" } ], + "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "ThresholdMetric retrieves raw values for an [SLOObjective](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective) to compare with its Operator and Value.", "childrenPaths": [ "$.spec.thresholdMetric.metricSource" ] @@ -8772,6 +9698,7 @@ "errorCode": "required" } ], + "typeDoc": "SLIMetricSource identifies a metrics backend and supplies the configuration needed to retrieve a metric.", "childrenPaths": [ "$.spec.thresholdMetric.metricSource.metricSourceRef", "$.spec.thresholdMetric.metricSource.type", @@ -8802,14 +9729,16 @@ "123-abc" ] } - ] + ], + "fieldDoc": "MetricSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource)." }, { "path": "$.spec.thresholdMetric.metricSource.type", "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." }, { "path": "$.spec.thresholdMetric.metricSource.spec", @@ -8827,6 +9756,7 @@ "errorCode": "map_min_length" } ], + "fieldDoc": "Spec contains source-specific query or metric-retrieval configuration.", "childrenPaths": [ "$.spec.thresholdMetric.metricSource.spec.*~", "$.spec.thresholdMetric.metricSource.spec.*" @@ -8859,7 +9789,7 @@ "errorCode": "optional" }, { - "description": "properties are mutually exclusive: raw, total", + "description": "exactly one of 'total' and 'raw' must be set", "errorCode": "mutually_exclusive" }, { @@ -8868,9 +9798,13 @@ }, { "description": "one of [bad, good] properties must be set, none was provided", - "errorCode": "one_of_properties" + "errorCode": "one_of_properties", + "conditions": [ + "'total' is set" + ] } ], + "typeDoc": "SLIRatioMetric defines a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 99 good events out of 100 produce a ratio of 0.99. One bad event out of 100 produces the same ratio.", "childrenPaths": [ "$.spec.ratioMetric.counter", "$.spec.ratioMetric.good", @@ -8885,7 +9819,8 @@ "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "Counter reports whether the queried good, bad, and total metrics are monotonically increasing. It has no effect when Raw is used." }, { "path": "$.spec.ratioMetric.good", @@ -8897,9 +9832,15 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'total' is set", + "'good' is set" + ] } ], + "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "Good supplies the numerator for a good-over-total ratio.", "childrenPaths": [ "$.spec.ratioMetric.good.metricSource" ] @@ -8914,9 +9855,14 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'total' is set", + "'good' is set" + ] } ], + "typeDoc": "SLIMetricSource identifies a metrics backend and supplies the configuration needed to retrieve a metric.", "childrenPaths": [ "$.spec.ratioMetric.good.metricSource.metricSourceRef", "$.spec.ratioMetric.good.metricSource.type", @@ -8932,29 +9878,43 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'total' is set", + "'good' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'total' is set", + "'good' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'total' is set", + "'good' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "MetricSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource)." }, { "path": "$.spec.ratioMetric.good.metricSource.type", "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." }, { "path": "$.spec.ratioMetric.good.metricSource.spec", @@ -8965,13 +9925,22 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'total' is set", + "'good' is set" + ] }, { "description": "length must be greater than or equal to 1", - "errorCode": "map_min_length" + "errorCode": "map_min_length", + "conditions": [ + "'total' is set", + "'good' is set" + ] } ], + "fieldDoc": "Spec contains source-specific query or metric-retrieval configuration.", "childrenPaths": [ "$.spec.ratioMetric.good.metricSource.spec.*~", "$.spec.ratioMetric.good.metricSource.spec.*" @@ -9001,9 +9970,15 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'total' is set", + "'bad' is set" + ] } ], + "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "Bad supplies the number subtracted from Total for a failure-based ratio.", "childrenPaths": [ "$.spec.ratioMetric.bad.metricSource" ] @@ -9018,9 +9993,14 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'total' is set", + "'bad' is set" + ] } ], + "typeDoc": "SLIMetricSource identifies a metrics backend and supplies the configuration needed to retrieve a metric.", "childrenPaths": [ "$.spec.ratioMetric.bad.metricSource.metricSourceRef", "$.spec.ratioMetric.bad.metricSource.type", @@ -9036,29 +10016,43 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'total' is set", + "'bad' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'total' is set", + "'bad' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'total' is set", + "'bad' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "MetricSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource)." }, { "path": "$.spec.ratioMetric.bad.metricSource.type", "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." }, { "path": "$.spec.ratioMetric.bad.metricSource.spec", @@ -9069,13 +10063,22 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'total' is set", + "'bad' is set" + ] }, { "description": "length must be greater than or equal to 1", - "errorCode": "map_min_length" + "errorCode": "map_min_length", + "conditions": [ + "'total' is set", + "'bad' is set" + ] } ], + "fieldDoc": "Spec contains source-specific query or metric-retrieval configuration.", "childrenPaths": [ "$.spec.ratioMetric.bad.metricSource.spec.*~", "$.spec.ratioMetric.bad.metricSource.spec.*" @@ -9105,9 +10108,14 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'total' is set" + ] } ], + "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "Total supplies the denominator for a Good- or Bad-based ratio.", "childrenPaths": [ "$.spec.ratioMetric.total.metricSource" ] @@ -9122,9 +10130,13 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'total' is set" + ] } ], + "typeDoc": "SLIMetricSource identifies a metrics backend and supplies the configuration needed to retrieve a metric.", "childrenPaths": [ "$.spec.ratioMetric.total.metricSource.metricSourceRef", "$.spec.ratioMetric.total.metricSource.type", @@ -9140,29 +10152,40 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'total' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'total' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'total' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "MetricSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource)." }, { "path": "$.spec.ratioMetric.total.metricSource.type", "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." }, { "path": "$.spec.ratioMetric.total.metricSource.spec", @@ -9173,13 +10196,20 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'total' is set" + ] }, { "description": "length must be greater than or equal to 1", - "errorCode": "map_min_length" + "errorCode": "map_min_length", + "conditions": [ + "'total' is set" + ] } ], + "fieldDoc": "Spec contains source-specific query or metric-retrieval configuration.", "childrenPaths": [ "$.spec.ratioMetric.total.metricSource.spec.*~", "$.spec.ratioMetric.total.metricSource.spec.*" @@ -9213,13 +10243,21 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'raw' is set" + ] }, { "description": "must be one of: success, failure", - "errorCode": "one_of" + "errorCode": "one_of", + "conditions": [ + "'raw' is set" + ] } - ] + ], + "typeDoc": "SLIRawMetricType identifies how a precomputed raw ratio is interpreted.", + "fieldDoc": "RawType selects whether Raw is interpreted as a success or failure ratio when Raw is used." }, { "path": "$.spec.ratioMetric.raw", @@ -9231,9 +10269,14 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'raw' is set" + ] } ], + "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "Raw retrieves a precomputed ratio.", "childrenPaths": [ "$.spec.ratioMetric.raw.metricSource" ] @@ -9248,9 +10291,13 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'raw' is set" + ] } ], + "typeDoc": "SLIMetricSource identifies a metrics backend and supplies the configuration needed to retrieve a metric.", "childrenPaths": [ "$.spec.ratioMetric.raw.metricSource.metricSourceRef", "$.spec.ratioMetric.raw.metricSource.type", @@ -9266,29 +10313,40 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'raw' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'raw' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'raw' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "MetricSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource)." }, { "path": "$.spec.ratioMetric.raw.metricSource.type", "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." }, { "path": "$.spec.ratioMetric.raw.metricSource.spec", @@ -9299,13 +10357,20 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'raw' is set" + ] }, { "description": "length must be greater than or equal to 1", - "errorCode": "map_min_length" + "errorCode": "map_min_length", + "conditions": [ + "'raw' is set" + ] } ], + "fieldDoc": "Spec contains source-specific query or metric-retrieval configuration.", "childrenPaths": [ "$.spec.ratioMetric.raw.metricSource.spec.*~", "$.spec.ratioMetric.raw.metricSource.spec.*" @@ -9337,6 +10402,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "SLO represents a target value or range for a service level measured by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -9401,6 +10467,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata identifies and describes an OpenSLO v1 object.", "childrenPaths": [ "$.metadata.name", "$.metadata.displayName", @@ -9432,7 +10499,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object." }, { "path": "$.metadata.displayName", @@ -9449,7 +10517,8 @@ "description": "length must be less than or equal to 63", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "DisplayName is the object's human-readable name." }, { "path": "$.metadata.labels", @@ -9458,6 +10527,8 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Labels maps metadata keys to one or more values.", + "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*", @@ -9499,6 +10570,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", + "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -9549,10 +10622,14 @@ "errorCode": "mutually_exclusive" }, { - "description": "properties are mutually exclusive: indicator, indicatorRef", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'indicator' and 'indicatorRef' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] } ], + "typeDoc": "SLOSpec defines the service association, indicator placement, budgeting method, evaluation window, objectives, and alert policies of an [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO).", "childrenPaths": [ "$.spec.description", "$.spec.service", @@ -9578,7 +10655,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the SLO." }, { "path": "$.spec.service", @@ -9591,7 +10669,8 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Service names the associated service. Consumers define how to resolve the name to a [Service](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#Service)." }, { "path": "$.spec.indicator", @@ -9603,9 +10682,14 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] } ], + "typeDoc": "SLOIndicatorInline embeds an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI) in an [SLOSpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOSpec) or [SLOObjective](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective).", + "fieldDoc": "Indicator defines a standard SLO's SLI inline. Composite SLOs place indicators on individual Objectives.", "childrenPaths": [ "$.spec.indicator.metadata", "$.spec.indicator.spec" @@ -9621,9 +10705,13 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] } ], + "typeDoc": "Metadata identifies and describes an OpenSLO v1 object.", "childrenPaths": [ "$.spec.indicator.metadata.name", "$.spec.indicator.metadata.displayName", @@ -9640,22 +10728,32 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object." }, { "path": "$.spec.indicator.metadata.displayName", @@ -9666,13 +10764,20 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "length must be less than or equal to 63", - "errorCode": "string_max_length" + "errorCode": "string_max_length", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] } - ] + ], + "fieldDoc": "DisplayName is the object's human-readable name." }, { "path": "$.spec.indicator.metadata.labels", @@ -9681,6 +10786,8 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Labels maps metadata keys to one or more values.", + "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", "childrenPaths": [ "$.spec.indicator.metadata.labels.*~", "$.spec.indicator.metadata.labels.*", @@ -9696,7 +10803,10 @@ "rules": [ { "description": "string must match regular expression: '^[a-zA-Z0-9]([-._a-zA-Z0-9]{0,61}[a-zA-Z0-9])?$'", - "errorCode": "string_match_regexp" + "errorCode": "string_match_regexp", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] } ] }, @@ -9722,6 +10832,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", + "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ "$.spec.indicator.metadata.annotations.*~", "$.spec.indicator.metadata.annotations.*" @@ -9736,11 +10848,17 @@ "rules": [ { "description": "string must match regular expression: '^(.{0,253}/)?.{0,63}$'", - "errorCode": "string_match_regexp" + "errorCode": "string_match_regexp", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "string must match regular expression: '^([a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?)*/)?[a-zA-Z0-9]([-._a-zA-Z0-9]{0,61}[a-zA-Z0-9])?$'", "errorCode": "string_match_regexp", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ], "examples": [ "my-domain.org/my-key", "openslo.com/annotation" @@ -9764,10 +10882,14 @@ }, "rules": [ { - "description": "properties are mutually exclusive: ratioMetric, thresholdMetric", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'thresholdMetric' and 'ratioMetric' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] } ], + "typeDoc": "SLISpec defines the metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", "childrenPaths": [ "$.spec.indicator.spec.description", "$.spec.indicator.spec.thresholdMetric", @@ -9783,9 +10905,13 @@ "rules": [ { "description": "length must be less than or equal to 1050", - "errorCode": "string_max_length" + "errorCode": "string_max_length", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] } - ] + ], + "fieldDoc": "Description summarizes the SLI." }, { "path": "$.spec.indicator.spec.thresholdMetric", @@ -9797,9 +10923,14 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] } ], + "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "ThresholdMetric retrieves raw values for an [SLOObjective](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective) to compare with its Operator and Value.", "childrenPaths": [ "$.spec.indicator.spec.thresholdMetric.metricSource" ] @@ -9814,9 +10945,13 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] } ], + "typeDoc": "SLIMetricSource identifies a metrics backend and supplies the configuration needed to retrieve a metric.", "childrenPaths": [ "$.spec.indicator.spec.thresholdMetric.metricSource.metricSourceRef", "$.spec.indicator.spec.thresholdMetric.metricSource.type", @@ -9832,29 +10967,40 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "MetricSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource)." }, { "path": "$.spec.indicator.spec.thresholdMetric.metricSource.type", "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." }, { "path": "$.spec.indicator.spec.thresholdMetric.metricSource.spec", @@ -9865,13 +11011,20 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "length must be greater than or equal to 1", - "errorCode": "map_min_length" + "errorCode": "map_min_length", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] } ], + "fieldDoc": "Spec contains source-specific query or metric-retrieval configuration.", "childrenPaths": [ "$.spec.indicator.spec.thresholdMetric.metricSource.spec.*~", "$.spec.indicator.spec.thresholdMetric.metricSource.spec.*" @@ -9901,21 +11054,35 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] }, { - "description": "properties are mutually exclusive: raw, total", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'total' and 'raw' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "properties are mutually exclusive: bad, good, raw", - "errorCode": "mutually_exclusive" + "errorCode": "mutually_exclusive", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "one of [bad, good] properties must be set, none was provided", - "errorCode": "one_of_properties" + "errorCode": "one_of_properties", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set" + ] } ], + "typeDoc": "SLIRatioMetric defines a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 99 good events out of 100 produce a ratio of 0.99. One bad event out of 100 produces the same ratio.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.counter", "$.spec.indicator.spec.ratioMetric.good", @@ -9930,7 +11097,8 @@ "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "Counter reports whether the queried good, bad, and total metrics are monotonically increasing. It has no effect when Raw is used." }, { "path": "$.spec.indicator.spec.ratioMetric.good", @@ -9942,9 +11110,16 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'good' is set" + ] } ], + "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "Good supplies the numerator for a good-over-total ratio.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.good.metricSource" ] @@ -9959,9 +11134,15 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'good' is set" + ] } ], + "typeDoc": "SLIMetricSource identifies a metrics backend and supplies the configuration needed to retrieve a metric.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.good.metricSource.metricSourceRef", "$.spec.indicator.spec.ratioMetric.good.metricSource.type", @@ -9977,29 +11158,46 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'good' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'good' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'good' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "MetricSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource)." }, { "path": "$.spec.indicator.spec.ratioMetric.good.metricSource.type", "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." }, { "path": "$.spec.indicator.spec.ratioMetric.good.metricSource.spec", @@ -10010,13 +11208,24 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'good' is set" + ] }, { "description": "length must be greater than or equal to 1", - "errorCode": "map_min_length" + "errorCode": "map_min_length", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'good' is set" + ] } ], + "fieldDoc": "Spec contains source-specific query or metric-retrieval configuration.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.good.metricSource.spec.*~", "$.spec.indicator.spec.ratioMetric.good.metricSource.spec.*" @@ -10046,9 +11255,16 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] } ], + "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "Bad supplies the number subtracted from Total for a failure-based ratio.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.bad.metricSource" ] @@ -10063,9 +11279,15 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] } ], + "typeDoc": "SLIMetricSource identifies a metrics backend and supplies the configuration needed to retrieve a metric.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.bad.metricSource.metricSourceRef", "$.spec.indicator.spec.ratioMetric.bad.metricSource.type", @@ -10081,29 +11303,46 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "MetricSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource)." }, { "path": "$.spec.indicator.spec.ratioMetric.bad.metricSource.type", "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." }, { "path": "$.spec.indicator.spec.ratioMetric.bad.metricSource.spec", @@ -10114,13 +11353,24 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] }, { "description": "length must be greater than or equal to 1", - "errorCode": "map_min_length" + "errorCode": "map_min_length", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] } ], + "fieldDoc": "Spec contains source-specific query or metric-retrieval configuration.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.bad.metricSource.spec.*~", "$.spec.indicator.spec.ratioMetric.bad.metricSource.spec.*" @@ -10150,9 +11400,15 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set" + ] } ], + "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "Total supplies the denominator for a Good- or Bad-based ratio.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.total.metricSource" ] @@ -10167,9 +11423,14 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set" + ] } ], + "typeDoc": "SLIMetricSource identifies a metrics backend and supplies the configuration needed to retrieve a metric.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.total.metricSource.metricSourceRef", "$.spec.indicator.spec.ratioMetric.total.metricSource.type", @@ -10185,29 +11446,43 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "MetricSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource)." }, { "path": "$.spec.indicator.spec.ratioMetric.total.metricSource.type", "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." }, { "path": "$.spec.indicator.spec.ratioMetric.total.metricSource.spec", @@ -10218,13 +11493,22 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set" + ] }, { "description": "length must be greater than or equal to 1", - "errorCode": "map_min_length" + "errorCode": "map_min_length", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'total' is set" + ] } ], + "fieldDoc": "Spec contains source-specific query or metric-retrieval configuration.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.total.metricSource.spec.*~", "$.spec.indicator.spec.ratioMetric.total.metricSource.spec.*" @@ -10258,13 +11542,23 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ] }, { "description": "must be one of: success, failure", - "errorCode": "one_of" + "errorCode": "one_of", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ] } - ] + ], + "typeDoc": "SLIRawMetricType identifies how a precomputed raw ratio is interpreted.", + "fieldDoc": "RawType selects whether Raw is interpreted as a success or failure ratio when Raw is used." }, { "path": "$.spec.indicator.spec.ratioMetric.raw", @@ -10276,9 +11570,15 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ] } ], + "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "Raw retrieves a precomputed ratio.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.raw.metricSource" ] @@ -10293,9 +11593,14 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ] } ], + "typeDoc": "SLIMetricSource identifies a metrics backend and supplies the configuration needed to retrieve a metric.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.raw.metricSource.metricSourceRef", "$.spec.indicator.spec.ratioMetric.raw.metricSource.type", @@ -10311,29 +11616,43 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "MetricSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource)." }, { "path": "$.spec.indicator.spec.ratioMetric.raw.metricSource.type", "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." }, { "path": "$.spec.indicator.spec.ratioMetric.raw.metricSource.spec", @@ -10344,13 +11663,22 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ] }, { "description": "length must be greater than or equal to 1", - "errorCode": "map_min_length" + "errorCode": "map_min_length", + "conditions": [ + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ] } ], + "fieldDoc": "Spec contains source-specific query or metric-retrieval configuration.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.raw.metricSource.spec.*~", "$.spec.indicator.spec.ratioMetric.raw.metricSource.spec.*" @@ -10379,22 +11707,32 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "IndicatorRef names an existing [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI) for a standard SLO. Composite SLOs place indicator references on individual Objectives." }, { "path": "$.spec.budgetingMethod", @@ -10417,7 +11755,9 @@ "description": "must be one of: Occurrences, Timeslices, RatioTimeslices", "errorCode": "one_of" } - ] + ], + "typeDoc": "SLOBudgetingMethod identifies how an [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO) calculates objective success and error-budget use. Occurrences counts good events. Timeslices counts slices that meet [SLOObjective.TimeSliceTarget](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective.TimeSliceTarget). RatioTimeslices averages success ratios across slices. Composite SLOs apply objective weights to each calculation.", + "fieldDoc": "BudgetingMethod applies the selected error-budget calculation to every objective." }, { "path": "$.spec.timeWindow", @@ -10431,7 +11771,8 @@ "description": "length must be between 1 and 1", "errorCode": "slice_length" } - ] + ], + "fieldDoc": "TimeWindow contains the SLO's evaluation window." }, { "path": "$.spec.timeWindow[*]", @@ -10440,6 +11781,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "SLOTimeWindow defines either a rolling or calendar-aligned evaluation window.", "childrenPaths": [ "$.spec.timeWindow[*].duration", "$.spec.timeWindow[*].isRolling", @@ -10459,14 +11801,16 @@ "errorCode": "required" } ], - "typeDoc": "DurationShorthand is a shorthand representation of time duration. It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days)." + "typeDoc": "DurationShorthand represents a duration as an integer and a case-sensitive [DurationShorthandUnit](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DurationShorthandUnit), such as \"1m\" or \"10d\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", + "fieldDoc": "Duration is the length of the evaluation window." }, { "path": "$.spec.timeWindow[*].isRolling", "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "IsRolling selects a rolling window when true and a calendar-aligned window when false." }, { "path": "$.spec.timeWindow[*].calendar", @@ -10481,6 +11825,8 @@ "errorCode": "optional" } ], + "typeDoc": "SLOCalendar anchors a calendar-aligned [SLOTimeWindow](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOTimeWindow) in a time zone.", + "fieldDoc": "Calendar defines the alignment of a calendar window.", "childrenPaths": [ "$.spec.timeWindow[*].calendar.startTime", "$.spec.timeWindow[*].calendar.timeZone" @@ -10498,7 +11844,8 @@ "details": "date and time format follows Go's time layout, see https://pkg.go.dev/time#Layout for more details", "errorCode": "string_date_time" } - ] + ], + "fieldDoc": "StartTime anchors the first calendar window." }, { "path": "$.spec.timeWindow[*].calendar.timeZone", @@ -10516,7 +11863,8 @@ "Europe/Warsaw" ] } - ] + ], + "fieldDoc": "TimeZone controls the interpretation of StartTime and later boundaries." }, { "path": "$.spec.objectives", @@ -10524,7 +11872,8 @@ "name": "[]SLOObjective", "kind": "[]struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" - } + }, + "fieldDoc": "Objectives contains the SLO's target definitions." }, { "path": "$.spec.objectives[*]", @@ -10535,14 +11884,19 @@ }, "rules": [ { - "description": "properties are mutually exclusive: target, targetPercent", + "description": "exactly one of 'target' and 'targetPercent' must be set", "errorCode": "mutually_exclusive" }, { - "description": "properties are mutually exclusive: indicator, indicatorRef", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'indicator' and 'indicatorRef' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] } ], + "typeDoc": "SLOObjective defines a success target and, when applicable, a threshold comparison or composite-specific indicator. For example, Target 0.995 and TargetPercent 99.5 both express a 99.5 percent target.", "childrenPaths": [ "$.spec.objectives[*].displayName", "$.spec.objectives[*].op", @@ -10561,7 +11915,8 @@ "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "DisplayName is the objective's human-readable name." }, { "path": "$.spec.objectives[*].op", @@ -10598,7 +11953,9 @@ "'indicator.spec.thresholdMetric' is set" ] } - ] + ], + "typeDoc": "Operator identifies a comparison applied to a metric value.", + "fieldDoc": "Operator compares threshold-metric samples with Value." }, { "path": "$.spec.objectives[*].value", @@ -10621,7 +11978,8 @@ "'indicator.spec.thresholdMetric' is set" ] } - ] + ], + "fieldDoc": "Value sets the threshold for metric sample comparisons. It is distinct from the success target expressed by Target or TargetPercent." }, { "path": "$.spec.objectives[*].target", @@ -10642,7 +12000,8 @@ "description": "must be less than '1'", "errorCode": "less_than" } - ] + ], + "fieldDoc": "Target expresses the success target as a fraction." }, { "path": "$.spec.objectives[*].targetPercent", @@ -10663,7 +12022,8 @@ "description": "must be less than '100'", "errorCode": "less_than" } - ] + ], + "fieldDoc": "TargetPercent expresses the success target as a percentage." }, { "path": "$.spec.objectives[*].timeSliceTarget", @@ -10674,17 +12034,27 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'budgetingMethod' is 'Timeslices'" + ] }, { "description": "must be greater than '0'", - "errorCode": "greater_than" + "errorCode": "greater_than", + "conditions": [ + "'budgetingMethod' is 'Timeslices'" + ] }, { "description": "must be less than or equal to '1'", - "errorCode": "less_than_or_equal_to" + "errorCode": "less_than_or_equal_to", + "conditions": [ + "'budgetingMethod' is 'Timeslices'" + ] } - ] + ], + "fieldDoc": "TimeSliceTarget classifies a slice as good when BudgetingMethod is [SLOBudgetingMethodTimeslices](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOBudgetingMethodTimeslices)." }, { "path": "$.spec.objectives[*].timeSliceWindow", @@ -10696,10 +12066,21 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'budgetingMethod' is 'Timeslices'" + ] + }, + { + "description": "property is required", + "errorCode": "required", + "conditions": [ + "'budgetingMethod' is 'RatioTimeslices'" + ] } ], - "typeDoc": "DurationShorthand is a shorthand representation of time duration. It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days)." + "typeDoc": "DurationShorthand represents a duration as an integer and a case-sensitive [DurationShorthandUnit](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DurationShorthandUnit), such as \"1m\" or \"10d\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", + "fieldDoc": "TimeSliceWindow sets the slice size and query-evaluation interval for [SLOBudgetingMethodTimeslices](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOBudgetingMethodTimeslices) and [SLOBudgetingMethodRatioTimeslices](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOBudgetingMethodRatioTimeslices)." }, { "path": "$.spec.objectives[*].indicator", @@ -10711,9 +12092,15 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] } ], + "typeDoc": "SLOIndicatorInline embeds an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI) in an [SLOSpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOSpec) or [SLOObjective](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective).", + "fieldDoc": "Indicator defines this objective's SLI inline for a composite SLO.", "childrenPaths": [ "$.spec.objectives[*].indicator.metadata", "$.spec.objectives[*].indicator.spec" @@ -10729,9 +12116,14 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] } ], + "typeDoc": "Metadata identifies and describes an OpenSLO v1 object.", "childrenPaths": [ "$.spec.objectives[*].indicator.metadata.name", "$.spec.objectives[*].indicator.metadata.displayName", @@ -10748,22 +12140,35 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object." }, { "path": "$.spec.objectives[*].indicator.metadata.displayName", @@ -10774,13 +12179,22 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "length must be less than or equal to 63", - "errorCode": "string_max_length" + "errorCode": "string_max_length", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] } - ] + ], + "fieldDoc": "DisplayName is the object's human-readable name." }, { "path": "$.spec.objectives[*].indicator.metadata.labels", @@ -10789,6 +12203,8 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Labels maps metadata keys to one or more values.", + "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", "childrenPaths": [ "$.spec.objectives[*].indicator.metadata.labels.*~", "$.spec.objectives[*].indicator.metadata.labels.*", @@ -10804,7 +12220,11 @@ "rules": [ { "description": "string must match regular expression: '^[a-zA-Z0-9]([-._a-zA-Z0-9]{0,61}[a-zA-Z0-9])?$'", - "errorCode": "string_match_regexp" + "errorCode": "string_match_regexp", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] } ] }, @@ -10830,6 +12250,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", + "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ "$.spec.objectives[*].indicator.metadata.annotations.*~", "$.spec.objectives[*].indicator.metadata.annotations.*" @@ -10844,11 +12266,19 @@ "rules": [ { "description": "string must match regular expression: '^(.{0,253}/)?.{0,63}$'", - "errorCode": "string_match_regexp" + "errorCode": "string_match_regexp", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "string must match regular expression: '^([a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?)*/)?[a-zA-Z0-9]([-._a-zA-Z0-9]{0,61}[a-zA-Z0-9])?$'", "errorCode": "string_match_regexp", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ], "examples": [ "my-domain.org/my-key", "openslo.com/annotation" @@ -10872,10 +12302,15 @@ }, "rules": [ { - "description": "properties are mutually exclusive: ratioMetric, thresholdMetric", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'thresholdMetric' and 'ratioMetric' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] } ], + "typeDoc": "SLISpec defines the metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.description", "$.spec.objectives[*].indicator.spec.thresholdMetric", @@ -10891,9 +12326,14 @@ "rules": [ { "description": "length must be less than or equal to 1050", - "errorCode": "string_max_length" + "errorCode": "string_max_length", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] } - ] + ], + "fieldDoc": "Description summarizes the SLI." }, { "path": "$.spec.objectives[*].indicator.spec.thresholdMetric", @@ -10905,9 +12345,15 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] } ], + "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "ThresholdMetric retrieves raw values for an [SLOObjective](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective) to compare with its Operator and Value.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.thresholdMetric.metricSource" ] @@ -10922,9 +12368,14 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] } ], + "typeDoc": "SLIMetricSource identifies a metrics backend and supplies the configuration needed to retrieve a metric.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.thresholdMetric.metricSource.metricSourceRef", "$.spec.objectives[*].indicator.spec.thresholdMetric.metricSource.type", @@ -10940,29 +12391,43 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "MetricSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource)." }, { "path": "$.spec.objectives[*].indicator.spec.thresholdMetric.metricSource.type", "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." }, { "path": "$.spec.objectives[*].indicator.spec.thresholdMetric.metricSource.spec", @@ -10973,13 +12438,22 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "length must be greater than or equal to 1", - "errorCode": "map_min_length" + "errorCode": "map_min_length", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] } ], + "fieldDoc": "Spec contains source-specific query or metric-retrieval configuration.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.thresholdMetric.metricSource.spec.*~", "$.spec.objectives[*].indicator.spec.thresholdMetric.metricSource.spec.*" @@ -11009,21 +12483,39 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] }, { - "description": "properties are mutually exclusive: raw, total", - "errorCode": "mutually_exclusive" + "description": "exactly one of 'total' and 'raw' must be set", + "errorCode": "mutually_exclusive", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "properties are mutually exclusive: bad, good, raw", - "errorCode": "mutually_exclusive" + "errorCode": "mutually_exclusive", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "one of [bad, good] properties must be set, none was provided", - "errorCode": "one_of_properties" + "errorCode": "one_of_properties", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set" + ] } ], + "typeDoc": "SLIRatioMetric defines a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 99 good events out of 100 produce a ratio of 0.99. One bad event out of 100 produces the same ratio.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.counter", "$.spec.objectives[*].indicator.spec.ratioMetric.good", @@ -11038,7 +12530,8 @@ "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "Counter reports whether the queried good, bad, and total metrics are monotonically increasing. It has no effect when Raw is used." }, { "path": "$.spec.objectives[*].indicator.spec.ratioMetric.good", @@ -11050,9 +12543,17 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] } ], + "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "Good supplies the numerator for a good-over-total ratio.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.good.metricSource" ] @@ -11067,9 +12568,16 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] } ], + "typeDoc": "SLIMetricSource identifies a metrics backend and supplies the configuration needed to retrieve a metric.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.good.metricSource.metricSourceRef", "$.spec.objectives[*].indicator.spec.ratioMetric.good.metricSource.type", @@ -11085,29 +12593,49 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "MetricSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource)." }, { "path": "$.spec.objectives[*].indicator.spec.ratioMetric.good.metricSource.type", "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." }, { "path": "$.spec.objectives[*].indicator.spec.ratioMetric.good.metricSource.spec", @@ -11118,13 +12646,26 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] }, { "description": "length must be greater than or equal to 1", - "errorCode": "map_min_length" + "errorCode": "map_min_length", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] } ], + "fieldDoc": "Spec contains source-specific query or metric-retrieval configuration.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.good.metricSource.spec.*~", "$.spec.objectives[*].indicator.spec.ratioMetric.good.metricSource.spec.*" @@ -11154,9 +12695,17 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] } ], + "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "Bad supplies the number subtracted from Total for a failure-based ratio.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.bad.metricSource" ] @@ -11171,9 +12720,16 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] } ], + "typeDoc": "SLIMetricSource identifies a metrics backend and supplies the configuration needed to retrieve a metric.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.bad.metricSource.metricSourceRef", "$.spec.objectives[*].indicator.spec.ratioMetric.bad.metricSource.type", @@ -11189,29 +12745,49 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "MetricSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource)." }, { "path": "$.spec.objectives[*].indicator.spec.ratioMetric.bad.metricSource.type", "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." }, { "path": "$.spec.objectives[*].indicator.spec.ratioMetric.bad.metricSource.spec", @@ -11222,13 +12798,26 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] }, { "description": "length must be greater than or equal to 1", - "errorCode": "map_min_length" + "errorCode": "map_min_length", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set", + "'bad' is set" + ] } ], + "fieldDoc": "Spec contains source-specific query or metric-retrieval configuration.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.bad.metricSource.spec.*~", "$.spec.objectives[*].indicator.spec.ratioMetric.bad.metricSource.spec.*" @@ -11258,9 +12847,16 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set" + ] } ], + "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "Total supplies the denominator for a Good- or Bad-based ratio.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.total.metricSource" ] @@ -11275,9 +12871,15 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set" + ] } ], + "typeDoc": "SLIMetricSource identifies a metrics backend and supplies the configuration needed to retrieve a metric.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.total.metricSource.metricSourceRef", "$.spec.objectives[*].indicator.spec.ratioMetric.total.metricSource.type", @@ -11293,29 +12895,46 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "MetricSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource)." }, { "path": "$.spec.objectives[*].indicator.spec.ratioMetric.total.metricSource.type", "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." }, { "path": "$.spec.objectives[*].indicator.spec.ratioMetric.total.metricSource.spec", @@ -11326,13 +12945,24 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set" + ] }, { "description": "length must be greater than or equal to 1", - "errorCode": "map_min_length" + "errorCode": "map_min_length", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'total' is set" + ] } ], + "fieldDoc": "Spec contains source-specific query or metric-retrieval configuration.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.total.metricSource.spec.*~", "$.spec.objectives[*].indicator.spec.ratioMetric.total.metricSource.spec.*" @@ -11366,13 +12996,25 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ] }, { "description": "must be one of: success, failure", - "errorCode": "one_of" + "errorCode": "one_of", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ] } - ] + ], + "typeDoc": "SLIRawMetricType identifies how a precomputed raw ratio is interpreted.", + "fieldDoc": "RawType selects whether Raw is interpreted as a success or failure ratio when Raw is used." }, { "path": "$.spec.objectives[*].indicator.spec.ratioMetric.raw", @@ -11384,9 +13026,16 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ] } ], + "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "Raw retrieves a precomputed ratio.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.raw.metricSource" ] @@ -11401,9 +13050,15 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ] } ], + "typeDoc": "SLIMetricSource identifies a metrics backend and supplies the configuration needed to retrieve a metric.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.raw.metricSource.metricSourceRef", "$.spec.objectives[*].indicator.spec.ratioMetric.raw.metricSource.type", @@ -11419,29 +13074,46 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "MetricSourceRef names an existing [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource)." }, { "path": "$.spec.objectives[*].indicator.spec.ratioMetric.raw.metricSource.type", "typeInfo": { "name": "string", "kind": "string" - } + }, + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." }, { "path": "$.spec.objectives[*].indicator.spec.ratioMetric.raw.metricSource.spec", @@ -11452,13 +13124,24 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ] }, { "description": "length must be greater than or equal to 1", - "errorCode": "map_min_length" + "errorCode": "map_min_length", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set", + "'raw' is set" + ] } ], + "fieldDoc": "Spec contains source-specific query or metric-retrieval configuration.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.raw.metricSource.spec.*~", "$.spec.objectives[*].indicator.spec.ratioMetric.raw.metricSource.spec.*" @@ -11487,22 +13170,35 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length" + "errorCode": "string_dns_label:string_length", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ], "examples": [ "my-name", "123-abc" ] } - ] + ], + "fieldDoc": "IndicatorRef names this objective's [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI) for a composite SLO." }, { "path": "$.spec.objectives[*].compositeWeight", @@ -11513,13 +13209,20 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "is composite SLO" + ] }, { "description": "must be greater than '0'", - "errorCode": "greater_than" + "errorCode": "greater_than", + "conditions": [ + "is composite SLO" + ] } - ] + ], + "fieldDoc": "CompositeWeight scales this objective's contribution to a multi-objective composite SLO. OpenSLO uses a weight of 1 when omitted. This SDK leaves the field unset." }, { "path": "$.spec.alertPolicies", @@ -11527,7 +13230,8 @@ "name": "[]SLOAlertPolicy", "kind": "[]struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" - } + }, + "fieldDoc": "AlertPolicies contains inline alert policies or references to existing [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicy) objects." }, { "path": "$.spec.alertPolicies[*]", @@ -11538,7 +13242,7 @@ }, "rules": [ { - "description": "properties are mutually exclusive: spec, targetRef", + "description": "exactly one of 'alertPolicyRef' and 'spec' must be set", "errorCode": "mutually_exclusive" }, { @@ -11546,6 +13250,7 @@ "errorCode": "optional" } ], + "typeDoc": "SLOAlertPolicy associates an alert policy with an [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO).", "childrenPaths": [ "$.spec.alertPolicies[*].kind", "$.spec.alertPolicies[*].metadata", @@ -11588,6 +13293,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata identifies and describes an OpenSLO v1 object.", "childrenPaths": [ "$.spec.alertPolicies[*].metadata.name", "$.spec.alertPolicies[*].metadata.displayName", @@ -11619,7 +13325,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object." }, { "path": "$.spec.alertPolicies[*].metadata.displayName", @@ -11636,7 +13343,8 @@ "description": "length must be less than or equal to 63", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "DisplayName is the object's human-readable name." }, { "path": "$.spec.alertPolicies[*].metadata.labels", @@ -11645,6 +13353,8 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Labels maps metadata keys to one or more values.", + "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", "childrenPaths": [ "$.spec.alertPolicies[*].metadata.labels.*~", "$.spec.alertPolicies[*].metadata.labels.*", @@ -11686,6 +13396,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", + "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ "$.spec.alertPolicies[*].metadata.annotations.*~", "$.spec.alertPolicies[*].metadata.annotations.*" @@ -11732,6 +13444,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertPolicySpec defines the events that emit notifications and the condition and destinations used by an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicy).", "childrenPaths": [ "$.spec.alertPolicies[*].spec.description", "$.spec.alertPolicies[*].spec.alertWhenNoData", @@ -11754,28 +13467,32 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the alert policy." }, { "path": "$.spec.alertPolicies[*].spec.alertWhenNoData", "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "AlertWhenNoData enables alerts when the associated SLO has no burn-rate value." }, { "path": "$.spec.alertPolicies[*].spec.alertWhenBreaching", "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "AlertWhenBreaching enables alerts when the condition starts breaching." }, { "path": "$.spec.alertPolicies[*].spec.alertWhenResolved", "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "AlertWhenResolved enables alerts when the condition resolves." }, { "path": "$.spec.alertPolicies[*].spec.conditions", @@ -11789,7 +13506,8 @@ "description": "length must be between 1 and 1", "errorCode": "slice_length" } - ] + ], + "fieldDoc": "Conditions contains the alert condition, inline or by reference." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*]", @@ -11800,7 +13518,7 @@ }, "rules": [ { - "description": "properties are mutually exclusive: conditionRef, spec", + "description": "exactly one of 'conditionRef' and 'spec' must be set", "errorCode": "mutually_exclusive" }, { @@ -11808,6 +13526,7 @@ "errorCode": "optional" } ], + "typeDoc": "AlertPolicyCondition supplies an alert condition to an [AlertPolicySpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicySpec) by reference or inline definition.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].conditionRef", "$.spec.alertPolicies[*].spec.conditions[*].kind", @@ -11839,7 +13558,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "ConditionRef names an existing alert condition." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].kind", @@ -11876,6 +13596,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata identifies and describes an OpenSLO v1 object.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].metadata.name", "$.spec.alertPolicies[*].spec.conditions[*].metadata.displayName", @@ -11907,7 +13628,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].metadata.displayName", @@ -11924,7 +13646,8 @@ "description": "length must be less than or equal to 63", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "DisplayName is the object's human-readable name." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].metadata.labels", @@ -11933,6 +13656,8 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Labels maps metadata keys to one or more values.", + "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].metadata.labels.*~", "$.spec.alertPolicies[*].spec.conditions[*].metadata.labels.*", @@ -11974,6 +13699,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", + "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].metadata.annotations.*~", "$.spec.alertPolicies[*].spec.conditions[*].metadata.annotations.*" @@ -12020,6 +13747,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertConditionSpec defines an alert's severity and burn-rate condition.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].spec.severity", "$.spec.alertPolicies[*].spec.conditions[*].spec.condition", @@ -12037,7 +13765,8 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Severity is an implementation-defined classification such as \"sev1\" or \"page\"." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.condition", @@ -12052,6 +13781,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertConditionType defines a comparison against an SLO's burn rate. Burn rate is error-budget consumption relative to the rate allowed by the SLO.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].spec.condition.kind", "$.spec.alertPolicies[*].spec.conditions[*].spec.condition.op", @@ -12079,7 +13809,9 @@ "description": "must be one of: burnrate", "errorCode": "one_of" } - ] + ], + "typeDoc": "AlertConditionKind identifies the calculation used by an [AlertConditionType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertConditionType).", + "fieldDoc": "Kind selects the condition calculation. OpenSLO defaults Kind to [AlertConditionKindBurnRate](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertConditionKindBurnRate). This SDK does not apply that default." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.condition.op", @@ -12097,13 +13829,21 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] }, { "description": "must be one of: gt, lt, gte, lte", - "errorCode": "one_of" + "errorCode": "one_of", + "conditions": [ + "'kind' is 'burnrate'" + ] } - ] + ], + "typeDoc": "Operator identifies a comparison applied to a metric value.", + "fieldDoc": "Operator compares the calculated burn rate with Threshold." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.condition.threshold", @@ -12114,9 +13854,13 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] } - ] + ], + "fieldDoc": "Threshold sets the numeric burn-rate boundary." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.condition.lookbackWindow", @@ -12128,10 +13872,14 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'kind' is 'burnrate'" + ] } ], - "typeDoc": "DurationShorthand is a shorthand representation of time duration. It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days)." + "typeDoc": "DurationShorthand represents a duration as an integer and a case-sensitive [DurationShorthandUnit](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DurationShorthandUnit), such as \"1m\" or \"10d\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", + "fieldDoc": "LookbackWindow sets the period for burn-rate calculation." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.condition.alertAfter", @@ -12143,10 +13891,14 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional" + "errorCode": "optional", + "conditions": [ + "'kind' is 'burnrate'" + ] } ], - "typeDoc": "DurationShorthand is a shorthand representation of time duration. It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days)." + "typeDoc": "DurationShorthand represents a duration as an integer and a case-sensitive [DurationShorthandUnit](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DurationShorthandUnit), such as \"1m\" or \"10d\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", + "fieldDoc": "AlertAfter sets how long the comparison must hold before the condition breaches. OpenSLO treats an omitted value as \"0m\". This SDK leaves it unset." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.description", @@ -12159,7 +13911,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the alert condition." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets", @@ -12173,7 +13926,8 @@ "description": "length must be greater than or equal to 1", "errorCode": "slice_min_length" } - ] + ], + "fieldDoc": "NotificationTargets lists notification destinations, inline or by reference." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets[*]", @@ -12184,7 +13938,7 @@ }, "rules": [ { - "description": "properties are mutually exclusive: spec, targetRef", + "description": "exactly one of 'targetRef' and 'spec' must be set", "errorCode": "mutually_exclusive" }, { @@ -12192,6 +13946,7 @@ "errorCode": "optional" } ], + "typeDoc": "AlertPolicyNotificationTarget supplies a notification target to an [AlertPolicySpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicySpec) by reference or inline definition.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].targetRef", "$.spec.alertPolicies[*].spec.notificationTargets[*].kind", @@ -12223,7 +13978,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "TargetRef names an existing notification target." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets[*].kind", @@ -12260,6 +14016,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata identifies and describes an OpenSLO v1 object.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.name", "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.displayName", @@ -12291,7 +14048,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.displayName", @@ -12308,7 +14066,8 @@ "description": "length must be less than or equal to 63", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "DisplayName is the object's human-readable name." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.labels", @@ -12317,6 +14076,8 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Labels maps metadata keys to one or more values.", + "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.labels.*~", "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.labels.*", @@ -12358,6 +14119,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", + "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.annotations.*~", "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.annotations.*" @@ -12404,6 +14167,7 @@ "errorCode": "required" } ], + "typeDoc": "AlertNotificationTargetSpec defines an implementation-specific notification destination.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].spec.description", "$.spec.alertPolicies[*].spec.notificationTargets[*].spec.target" @@ -12420,7 +14184,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the destination." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets[*].spec.target", @@ -12433,7 +14198,8 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Target identifies the implementation-defined delivery target." }, { "path": "$.spec.alertPolicies[*].alertPolicyRef", @@ -12459,7 +14225,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "AlertPolicyRef names an existing alert policy." } ] }, @@ -12473,6 +14240,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Service groups related [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO) objects.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -12537,6 +14305,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata identifies and describes an OpenSLO v1 object.", "childrenPaths": [ "$.metadata.name", "$.metadata.displayName", @@ -12568,7 +14337,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name identifies the object." }, { "path": "$.metadata.displayName", @@ -12585,7 +14355,8 @@ "description": "length must be less than or equal to 63", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "DisplayName is the object's human-readable name." }, { "path": "$.metadata.labels", @@ -12594,6 +14365,8 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Labels maps metadata keys to one or more values.", + "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*", @@ -12635,6 +14408,8 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", + "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -12675,6 +14450,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "typeDoc": "ServiceSpec contains the descriptive properties of a [Service](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#Service).", "childrenPaths": [ "$.spec.description" ] @@ -12690,7 +14466,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the service." } ] } @@ -12706,6 +14483,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha" }, + "typeDoc": "SLO defines reliability targets for a service level measured by an indicator.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -12770,6 +14548,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata identifies an OpenSLO v1alpha object.", "childrenPaths": [ "$.metadata.name", "$.metadata.displayName" @@ -12799,7 +14578,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name is the object identifier used by references." }, { "path": "$.metadata.displayName", @@ -12816,7 +14596,8 @@ "description": "length must be less than or equal to 63", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "DisplayName is a human-readable name." }, { "path": "$.spec", @@ -12831,10 +14612,11 @@ "errorCode": "required" }, { - "description": "", + "description": "exactly one of 'indicator' and 'objectives[*].ratioMetrics' must be set", "errorCode": "mutually_exclusive" } ], + "typeDoc": "SLOSpec defines the service, indicator, objectives, time window, and error-budget calculation for an [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha#SLO).", "childrenPaths": [ "$.spec.timeWindows", "$.spec.timeWindows[*]", @@ -12852,7 +14634,14 @@ "name": "[]SLOTimeWindow", "kind": "[]struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha" - } + }, + "rules": [ + { + "description": "length must be between 1 and 1", + "errorCode": "slice_length" + } + ], + "fieldDoc": "TimeWindows contains the SLO evaluation window." }, { "path": "$.spec.timeWindows[*]", @@ -12861,6 +14650,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha" }, + "typeDoc": "SLOTimeWindow defines the period over which an SLO is evaluated. For example, a Unit of Week and a Count of 4 define a four-week window.", "childrenPaths": [ "$.spec.timeWindows[*].unit", "$.spec.timeWindows[*].count", @@ -12874,21 +14664,48 @@ "name": "SLOTimeWindowUnit", "kind": "string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha" - } + }, + "values": [ + "Second", + "Day", + "Week", + "Month", + "Quarter" + ], + "rules": [ + { + "description": "property is required", + "errorCode": "required" + }, + { + "description": "must be one of: Second, Day, Week, Month, Quarter", + "errorCode": "one_of" + } + ], + "typeDoc": "SLOTimeWindowUnit identifies the unit used to express an [SLOTimeWindow](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha#SLOTimeWindow).", + "fieldDoc": "Unit combines with Count to set the window length." }, { "path": "$.spec.timeWindows[*].count", "typeInfo": { "name": "int", "kind": "int" - } + }, + "rules": [ + { + "description": "must be greater than '0'", + "errorCode": "greater_than" + } + ], + "fieldDoc": "Count sets how many Units form the window." }, { "path": "$.spec.timeWindows[*].isRolling", "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "IsRolling selects a continuously advancing window when true and a calendar-aligned window when false." }, { "path": "$.spec.timeWindows[*].calendar", @@ -12897,6 +14714,14 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "typeDoc": "SLOCalendar anchors a calendar-aligned [SLOTimeWindow](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha#SLOTimeWindow).", + "fieldDoc": "Calendar defines the alignment of a calendar window.", "childrenPaths": [ "$.spec.timeWindows[*].calendar.startTime", "$.spec.timeWindows[*].calendar.timeZone" @@ -12907,14 +14732,34 @@ "typeInfo": { "name": "string", "kind": "string" - } + }, + "rules": [ + { + "description": "string must be a valid date and time in '2006-01-02 15:04:05' format", + "details": "date and time format follows Go's time layout, see https://pkg.go.dev/time#Layout for more details", + "errorCode": "string_date_time" + } + ], + "fieldDoc": "StartTime is the local anchor in YYYY-MM-DD HH:MM:SS format, for example 2026-07-25 14:30:00." }, { "path": "$.spec.timeWindows[*].calendar.timeZone", "typeInfo": { "name": "string", "kind": "string" - } + }, + "rules": [ + { + "description": "string must be a valid IANA Time Zone Database code", + "errorCode": "string_time_zone", + "examples": [ + "UTC", + "America/New_York", + "Europe/Warsaw" + ] + } + ], + "fieldDoc": "TimeZone is the IANA time-zone name used to interpret StartTime." }, { "path": "$.spec.budgetingMethod", @@ -12936,7 +14781,9 @@ "description": "must be one of: Occurrences, Timeslices", "errorCode": "one_of" } - ] + ], + "typeDoc": "SLOBudgetingMethod identifies how an SLO calculates its error budget. Occurrences weights each event equally. Timeslices weights each time slice equally.", + "fieldDoc": "BudgetingMethod applies the selected error-budget calculation to every objective." }, { "path": "$.spec.description", @@ -12949,7 +14796,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the SLO." }, { "path": "$.spec.indicator", @@ -12964,6 +14812,8 @@ "errorCode": "optional" } ], + "typeDoc": "SLOIndicator defines the threshold-metric form of a v1alpha service level indicator.", + "fieldDoc": "Indicator defines the threshold-metric form of the SLO.", "childrenPaths": [ "$.spec.indicator.thresholdMetric" ] @@ -12981,6 +14831,8 @@ "errorCode": "required" } ], + "typeDoc": "SLOMetricSourceSpec describes a provider-specific metric query.", + "fieldDoc": "ThresholdMetric retrieves raw metric values. Each objective compares them with its [Operator](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha#Operator) and Value.", "childrenPaths": [ "$.spec.indicator.thresholdMetric.source", "$.spec.indicator.thresholdMetric.queryType", @@ -13002,7 +14854,8 @@ "description": "string must match regular expression: '^[a-zA-Z]*$'", "errorCode": "string_alpha" } - ] + ], + "fieldDoc": "Source identifies the metric data source." }, { "path": "$.spec.indicator.thresholdMetric.queryType", @@ -13019,7 +14872,8 @@ "description": "string must match regular expression: '^[a-zA-Z]*$'", "errorCode": "string_alpha" } - ] + ], + "fieldDoc": "QueryType identifies the query language or query form." }, { "path": "$.spec.indicator.thresholdMetric.query", @@ -13036,7 +14890,8 @@ "description": "string must not be empty", "errorCode": "string_not_empty" } - ] + ], + "fieldDoc": "Query is the provider-specific expression that retrieves the metric." }, { "path": "$.spec.service", @@ -13049,7 +14904,8 @@ "description": "property is required", "errorCode": "required" } - ] + ], + "fieldDoc": "Service identifies the service whose reliability the SLO measures." }, { "path": "$.spec.objectives", @@ -13057,7 +14913,8 @@ "name": "[]SLOObjective", "kind": "[]struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha" - } + }, + "fieldDoc": "Objectives contains the targets used to evaluate the indicator." }, { "path": "$.spec.objectives[*]", @@ -13066,6 +14923,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha" }, + "typeDoc": "SLOObjective defines a reliability target and, for the ratio form, its metric queries.", "childrenPaths": [ "$.spec.objectives[*].displayName", "$.spec.objectives[*].value", @@ -13086,7 +14944,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "DisplayName is a human-readable objective name." }, { "path": "$.spec.objectives[*].value", @@ -13097,9 +14956,13 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'ratioMetrics' is not set" + ] } - ] + ], + "fieldDoc": "Value is the metric threshold used by [Operator](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha#Operator)." }, { "path": "$.spec.objectives[*].ratioMetrics", @@ -13114,6 +14977,8 @@ "errorCode": "optional" } ], + "typeDoc": "SLORatioMetrics defines an indicator as the ratio of good events to total events. For example, 99 successful requests out of 100 total requests produce a ratio of 0.99.", + "fieldDoc": "RatioMetrics supplies a good-events-to-total-events indicator.", "childrenPaths": [ "$.spec.objectives[*].ratioMetrics.good", "$.spec.objectives[*].ratioMetrics.total", @@ -13133,6 +14998,8 @@ "errorCode": "required" } ], + "typeDoc": "SLOMetricSourceSpec describes a provider-specific metric query.", + "fieldDoc": "Good retrieves the numerator: events considered successful.", "childrenPaths": [ "$.spec.objectives[*].ratioMetrics.good.source", "$.spec.objectives[*].ratioMetrics.good.queryType", @@ -13154,7 +15021,8 @@ "description": "string must match regular expression: '^[a-zA-Z]*$'", "errorCode": "string_alpha" } - ] + ], + "fieldDoc": "Source identifies the metric data source." }, { "path": "$.spec.objectives[*].ratioMetrics.good.queryType", @@ -13171,7 +15039,8 @@ "description": "string must match regular expression: '^[a-zA-Z]*$'", "errorCode": "string_alpha" } - ] + ], + "fieldDoc": "QueryType identifies the query language or query form." }, { "path": "$.spec.objectives[*].ratioMetrics.good.query", @@ -13188,7 +15057,8 @@ "description": "string must not be empty", "errorCode": "string_not_empty" } - ] + ], + "fieldDoc": "Query is the provider-specific expression that retrieves the metric." }, { "path": "$.spec.objectives[*].ratioMetrics.total", @@ -13203,6 +15073,8 @@ "errorCode": "required" } ], + "typeDoc": "SLOMetricSourceSpec describes a provider-specific metric query.", + "fieldDoc": "Total retrieves the denominator: all considered events.", "childrenPaths": [ "$.spec.objectives[*].ratioMetrics.total.source", "$.spec.objectives[*].ratioMetrics.total.queryType", @@ -13224,7 +15096,8 @@ "description": "string must match regular expression: '^[a-zA-Z]*$'", "errorCode": "string_alpha" } - ] + ], + "fieldDoc": "Source identifies the metric data source." }, { "path": "$.spec.objectives[*].ratioMetrics.total.queryType", @@ -13241,7 +15114,8 @@ "description": "string must match regular expression: '^[a-zA-Z]*$'", "errorCode": "string_alpha" } - ] + ], + "fieldDoc": "QueryType identifies the query language or query form." }, { "path": "$.spec.objectives[*].ratioMetrics.total.query", @@ -13258,14 +15132,16 @@ "description": "string must not be empty", "errorCode": "string_not_empty" } - ] + ], + "fieldDoc": "Query is the provider-specific expression that retrieves the metric." }, { "path": "$.spec.objectives[*].ratioMetrics.incremental", "typeInfo": { "name": "bool", "kind": "bool" - } + }, + "fieldDoc": "Incremental reports whether the queried metrics are monotonically increasing counters rather than values that can rise or fall." }, { "path": "$.spec.objectives[*].target", @@ -13286,7 +15162,8 @@ "description": "must be less than '1'", "errorCode": "less_than" } - ] + ], + "fieldDoc": "BudgetTarget is the desired fraction of good events or time slices." }, { "path": "$.spec.objectives[*].timeSliceTarget", @@ -13297,17 +15174,27 @@ "rules": [ { "description": "property is required", - "errorCode": "required" + "errorCode": "required", + "conditions": [ + "'budgetingMethod' is 'Timeslices'" + ] }, { "description": "must be greater than or equal to '0'", - "errorCode": "greater_than_or_equal_to" + "errorCode": "greater_than_or_equal_to", + "conditions": [ + "'budgetingMethod' is 'Timeslices'" + ] }, { "description": "must be less than or equal to '1'", - "errorCode": "less_than_or_equal_to" + "errorCode": "less_than_or_equal_to", + "conditions": [ + "'budgetingMethod' is 'Timeslices'" + ] } - ] + ], + "fieldDoc": "TimeSliceTarget is the minimum success ratio that makes a time slice good. It is used by the Timeslices budgeting method." }, { "path": "$.spec.objectives[*].op", @@ -13327,14 +15214,14 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "'thresholdMetric' is set" + "'ratioMetrics' is not set" ] }, { "description": "must be one of: gt, lt, gte, lte", "errorCode": "one_of", "conditions": [ - "'thresholdMetric' is set" + "'ratioMetrics' is not set" ] }, { @@ -13344,7 +15231,9 @@ "'ratioMetrics' is set" ] } - ] + ], + "typeDoc": "Operator selects the comparison between a threshold metric and an objective value.", + "fieldDoc": "Operator compares values returned by the threshold metric with Value." } ] }, @@ -13358,6 +15247,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha" }, + "typeDoc": "Service groups related SLOs.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -13422,6 +15312,7 @@ "errorCode": "required" } ], + "typeDoc": "Metadata identifies an OpenSLO v1alpha object.", "childrenPaths": [ "$.metadata.name", "$.metadata.displayName" @@ -13451,7 +15342,8 @@ "123-abc" ] } - ] + ], + "fieldDoc": "Name is the object identifier used by references." }, { "path": "$.metadata.displayName", @@ -13468,7 +15360,8 @@ "description": "length must be less than or equal to 63", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "DisplayName is a human-readable name." }, { "path": "$.spec", @@ -13477,6 +15370,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha" }, + "typeDoc": "ServiceSpec contains the descriptive properties of a [Service](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha#Service).", "childrenPaths": [ "$.spec.description" ] @@ -13492,7 +15386,8 @@ "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } - ] + ], + "fieldDoc": "Description summarizes the service." } ] } diff --git a/internal/cmd/objectdoc/go.mod b/internal/cmd/objectdoc/go.mod index d090985..ee6105b 100644 --- a/internal/cmd/objectdoc/go.mod +++ b/internal/cmd/objectdoc/go.mod @@ -6,13 +6,17 @@ require ( github.com/OpenSLO/go-sdk v0.8.0 github.com/nieomylnieja/govydoc v0.0.1 github.com/nobl9/govy v0.28.0 - golang.org/x/sync v0.22.0 + github.com/stretchr/testify v1.11.1 + golang.org/x/tools v0.48.0 ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/mod v0.38.0 // indirect + golang.org/x/sync v0.22.0 // indirect golang.org/x/text v0.38.0 // indirect - golang.org/x/tools v0.48.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/OpenSLO/go-sdk => ../../../. diff --git a/internal/cmd/objectdoc/go.sum b/internal/cmd/objectdoc/go.sum index 355a93e..2854174 100644 --- a/internal/cmd/objectdoc/go.sum +++ b/internal/cmd/objectdoc/go.sum @@ -20,6 +20,8 @@ golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= diff --git a/internal/cmd/objectdoc/main.go b/internal/cmd/objectdoc/main.go index 5295074..2578ad4 100644 --- a/internal/cmd/objectdoc/main.go +++ b/internal/cmd/objectdoc/main.go @@ -3,12 +3,24 @@ package main import ( "cmp" "encoding/json" + "errors" + "fmt" + "go/ast" + "go/doc/comment" + "go/types" + "io" + "maps" "os" + "reflect" + "regexp" "slices" + "strings" + "sync" "github.com/nieomylnieja/govydoc/pkg/govydoc" - "golang.org/x/sync/errgroup" + "golang.org/x/tools/go/packages" + "github.com/nobl9/govy/pkg/govy" "github.com/nobl9/govy/pkg/jsonpath" v1 "github.com/OpenSLO/go-sdk/pkg/openslo/v1" @@ -16,27 +28,33 @@ import ( "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" ) -var allDocsGeneratorFuncs = []func() (govydoc.ObjectDoc, error){ - func() (govydoc.ObjectDoc, error) { return govydoc.Generate(v1alpha.Service{}.GetValidator()) }, - func() (govydoc.ObjectDoc, error) { return govydoc.Generate(v1alpha.SLO{}.GetValidator()) }, - func() (govydoc.ObjectDoc, error) { return govydoc.Generate(v1.Service{}.GetValidator()) }, - func() (govydoc.ObjectDoc, error) { return govydoc.Generate(v1.SLO{}.GetValidator()) }, - func() (govydoc.ObjectDoc, error) { return govydoc.Generate(v1.SLI{}.GetValidator()) }, - func() (govydoc.ObjectDoc, error) { return govydoc.Generate(v1.AlertCondition{}.GetValidator()) }, - func() (govydoc.ObjectDoc, error) { - return govydoc.Generate(v1.AlertNotificationTarget{}.GetValidator()) - }, - func() (govydoc.ObjectDoc, error) { return govydoc.Generate(v1.AlertPolicy{}.GetValidator()) }, - func() (govydoc.ObjectDoc, error) { return govydoc.Generate(v1.DataSource{}.GetValidator()) }, - func() (govydoc.ObjectDoc, error) { return govydoc.Generate(v2alpha.Service{}.GetValidator()) }, - func() (govydoc.ObjectDoc, error) { return govydoc.Generate(v2alpha.SLO{}.GetValidator()) }, - func() (govydoc.ObjectDoc, error) { return govydoc.Generate(v2alpha.SLI{}.GetValidator()) }, - func() (govydoc.ObjectDoc, error) { return govydoc.Generate(v2alpha.AlertCondition{}.GetValidator()) }, - func() (govydoc.ObjectDoc, error) { - return govydoc.Generate(v2alpha.AlertNotificationTarget{}.GetValidator()) - }, - func() (govydoc.ObjectDoc, error) { return govydoc.Generate(v2alpha.AlertPolicy{}.GetValidator()) }, - func() (govydoc.ObjectDoc, error) { return govydoc.Generate(v2alpha.DataSource{}.GetValidator()) }, +type generatedObjectDoc struct { + doc govydoc.ObjectDoc + rootType reflect.Type +} + +type objectDocGenerator struct { + name string + generate func() (generatedObjectDoc, error) +} + +var allDocsGenerators = []objectDocGenerator{ + newObjectDocGenerator(v1alpha.Service{}.GetValidator()), + newObjectDocGenerator(v1alpha.SLO{}.GetValidator()), + newObjectDocGenerator(v1.Service{}.GetValidator()), + newObjectDocGenerator(v1.SLO{}.GetValidator()), + newObjectDocGenerator(v1.SLI{}.GetValidator()), + newObjectDocGenerator(v1.AlertCondition{}.GetValidator()), + newObjectDocGenerator(v1.AlertNotificationTarget{}.GetValidator()), + newObjectDocGenerator(v1.AlertPolicy{}.GetValidator()), + newObjectDocGenerator(v1.DataSource{}.GetValidator()), + newObjectDocGenerator(v2alpha.Service{}.GetValidator()), + newObjectDocGenerator(v2alpha.SLO{}.GetValidator()), + newObjectDocGenerator(v2alpha.SLI{}.GetValidator()), + newObjectDocGenerator(v2alpha.AlertCondition{}.GetValidator()), + newObjectDocGenerator(v2alpha.AlertNotificationTarget{}.GetValidator()), + newObjectDocGenerator(v2alpha.AlertPolicy{}.GetValidator()), + newObjectDocGenerator(v2alpha.DataSource{}.GetValidator()), } var ( @@ -52,53 +70,559 @@ type ( ) func main() { - docs := make([]govydoc.ObjectDoc, len(allDocsGeneratorFuncs)) - var group errgroup.Group - for i, generate := range allDocsGeneratorFuncs { - group.Go(func() error { - doc, err := generate() + versions, err := generateVersions() + if err != nil { + panic(err) + } + + if err = encodeVersions(os.Stdout, versions); err != nil { + panic(err) + } +} + +func encodeVersions(w io.Writer, versions Versions) error { + enc := json.NewEncoder(w) + enc.SetIndent("", " ") + return enc.Encode(versions) +} + +func newObjectDocGenerator[T any](validator govy.Validator[T]) objectDocGenerator { + typ := reflect.TypeFor[T]() + return objectDocGenerator{ + name: typ.String(), + generate: func() (generatedObjectDoc, error) { + return generateObjectDoc(validator) + }, + } +} + +func generateObjectDoc[T any](validator govy.Validator[T]) (generatedObjectDoc, error) { + rootType := reflect.TypeFor[T]() + doc, err := govydoc.Generate( + validator, + govydoc.GenerateGovyOptions(govy.PlanStrictMode()), + ) + if err != nil { + return generatedObjectDoc{}, err + } + if err = validateRuleDescriptions(doc, rootType); err != nil { + return generatedObjectDoc{}, err + } + return generatedObjectDoc{ + doc: doc, + rootType: rootType, + }, nil +} + +func validateRuleDescriptions(doc govydoc.ObjectDoc, rootType reflect.Type) error { + for _, property := range doc.Properties { + for ruleIndex, rule := range property.Rules { + if strings.TrimSpace(rule.Description) == "" { + return fmt.Errorf( + "validation rule %d for %s in %s has a blank description", + ruleIndex+1, + property.Path, + rootType, + ) + } + } + } + return nil +} + +func generateVersions() (Versions, error) { + docs, err := generateAllObjectDocs() + if err != nil { + return nil, err + } + if err = normalizeGeneratedDocs(docs); err != nil { + return nil, fmt.Errorf("normalize generated documentation: %w", err) + } + + slices.SortFunc(docs, func(o1, o2 generatedObjectDoc) int { + return cmp.Compare(o1.doc.Name, o2.doc.Name) + }) + return aggregateVersions(docs) +} + +func aggregateVersions(docs []generatedObjectDoc) (Versions, error) { + versions := make(Versions) + for _, generated := range docs { + doc := generated.doc + version, err := discriminatorValue(doc, apiVersionPath) + if err != nil { + return nil, err + } + kind, err := discriminatorValue(doc, kindPath) + if err != nil { + return nil, err + } + if versions[version] == nil { + versions[version] = make(map[Kind]govydoc.ObjectDoc) + } + if previous, exists := versions[version][kind]; exists { + return nil, fmt.Errorf( + "duplicate version and kind %q %q in document %q: pair is already used by %q", + version, + kind, + doc.Name, + previous.Name, + ) + } + versions[version][kind] = doc + } + return versions, nil +} + +func discriminatorValue(doc govydoc.ObjectDoc, path jsonpath.Path) (string, error) { + var value string + found := false + for _, property := range doc.Properties { + if !property.Path.Equal(path) { + continue + } + if found { + return "", fmt.Errorf( + "document %q has duplicate discriminator property %s", + doc.Name, + path, + ) + } + found = true + if len(property.Values) != 1 { + return "", fmt.Errorf( + "document %q discriminator property %s must have exactly one value, but it has %d", + doc.Name, + path, + len(property.Values), + ) + } + value = property.Values[0] + } + if !found { + return "", fmt.Errorf("document %q is missing discriminator property %s", doc.Name, path) + } + if value == "" { + return "", fmt.Errorf("document %q discriminator property %s has an empty value", doc.Name, path) + } + return value, nil +} + +func generateAllObjectDocs() ([]generatedObjectDoc, error) { + return generateObjectDocs(allDocsGenerators) +} + +func generateObjectDocs(generators []objectDocGenerator) ([]generatedObjectDoc, error) { + docs := make([]generatedObjectDoc, len(generators)) + generatorErrors := make([]error, len(generators)) + var wg sync.WaitGroup + for i, generator := range generators { + wg.Go(func() { + doc, err := generator.generate() if err != nil { - return err + generatorErrors[i] = fmt.Errorf("generate %s documentation: %w", generator.name, err) + return } docs[i] = doc - return nil }) } - if err := group.Wait(); err != nil { - panic(err) + wg.Wait() + if err := errors.Join(generatorErrors...); err != nil { + return nil, err } + return docs, nil +} + +const ( + docLinkBaseURL = "https://pkg.go.dev" + jsonRawMessageName = "RawMessage" + jsonRawMessageKind = "[]uint8" + jsonRawMessagePkg = "encoding/json" + jsonValueKind = "JSON" +) - slices.SortFunc(docs, func(o1, o2 govydoc.ObjectDoc) int { return cmp.Compare(o1.Name, o2.Name) }) +var deprecatedDocRegex = regexp.MustCompile(`(?m)^Deprecated:\s*(.*)$`) - versions := make(Versions) - for _, doc := range docs { - var ( - version Version - kind Kind +type fieldOrigin struct { + owner reflect.Type + field reflect.StructField +} + +type fieldDocKey struct { + packagePath string + typeName string + fieldName string +} + +type fieldDocResolver struct { + docs map[fieldDocKey]string +} + +func normalizeGeneratedDocs(docs []generatedObjectDoc) error { + origins := make([]map[string]fieldOrigin, len(docs)) + packagePaths := make(map[string]struct{}) + for i := range docs { + normalizeRawMessages(&docs[i].doc) + + mappedOrigins, err := mapJSONFieldOrigins(docs[i].rootType) + if err != nil { + return fmt.Errorf("map fields for %s: %w", docs[i].rootType, err) + } + origins[i] = mappedOrigins + for _, property := range docs[i].doc.Properties { + if property.FieldDoc != "" { + continue + } + origin, ok := origins[i][property.Path.String()] + if ok && origin.owner.PkgPath() != "" { + packagePaths[origin.owner.PkgPath()] = struct{}{} + } + } + } + + resolver, err := newFieldDocResolver(slices.Sorted(maps.Keys(packagePaths))) + if err != nil { + return err + } + var recoveryErrors []error + for i := range docs { + if err := recoverFieldDocs(&docs[i].doc, origins[i], resolver); err != nil { + recoveryErrors = append(recoveryErrors, err) + } + } + return errors.Join(recoveryErrors...) +} + +func normalizeRawMessages(doc *govydoc.ObjectDoc) { + wildcardPaths := make(map[string]struct{}) + for i := range doc.Properties { + property := &doc.Properties[i] + if property.TypeInfo.Name != jsonRawMessageName || + property.TypeInfo.Kind != jsonRawMessageKind || + property.TypeInfo.Package != jsonRawMessagePkg { + continue + } + property.TypeInfo.Kind = jsonValueKind + wildcardPaths[property.Path.IndexWildcard().String()] = struct{}{} + } + if len(wildcardPaths) == 0 { + return + } + + doc.Properties = slices.DeleteFunc(doc.Properties, func(property govydoc.PropertyDoc) bool { + _, remove := wildcardPaths[property.Path.String()] + return remove + }) + for i := range doc.Properties { + doc.Properties[i].ChildrenPaths = slices.DeleteFunc( + doc.Properties[i].ChildrenPaths, + func(path string) bool { + _, remove := wildcardPaths[path] + return remove + }, ) - for _, prop := range doc.Properties { - switch { - case prop.Path.Equal(apiVersionPath): - version = prop.Values[0] - case prop.Path.Equal(kindPath): - kind = prop.Values[0] + } +} + +func mapJSONFieldOrigins(root reflect.Type) (map[string]fieldOrigin, error) { + origins := make(map[string]fieldOrigin) + if err := walkJSONFields(root, jsonpath.NewRoot(), origins); err != nil { + return nil, err + } + return origins, nil +} + +func walkJSONFields( + typ reflect.Type, + path jsonpath.Path, + origins map[string]fieldOrigin, +) error { + typ = dereferenceType(typ) + switch typ.Kind() { + case reflect.Struct: + for _, visibleField := range reflect.VisibleFields(typ) { + if !visibleField.IsExported() { + continue + } + name, _, _ := strings.Cut(visibleField.Tag.Get("json"), ",") + if name == "" || name == "-" { + continue + } + fieldPath := path.Name(name) + origin, err := resolveFieldOrigin(typ, visibleField.Index) + if err != nil { + return err + } + pathString := fieldPath.String() + if previous, exists := origins[pathString]; exists { + return fmt.Errorf( + "JSON path %s resolves to both %s.%s and %s.%s", + pathString, + previous.owner, + previous.field.Name, + origin.owner, + origin.field.Name, + ) + } + origins[pathString] = origin + if err = walkJSONFields(visibleField.Type, fieldPath, origins); err != nil { + return err } } - if version == "" || kind == "" { - panic("missing version or kind in doc: " + doc.Name) + case reflect.Array, reflect.Slice: + return walkJSONFields(typ.Elem(), path.IndexWildcard(), origins) + case reflect.Map: + if err := walkJSONFields(typ.Key(), path.KeyWildcard(), origins); err != nil { + return err } - if versions[version] == nil { - versions[version] = make(map[Kind]govydoc.ObjectDoc) + return walkJSONFields(typ.Elem(), path.ValueWildcard(), origins) + default: + } + return nil +} + +func resolveFieldOrigin(typ reflect.Type, index []int) (fieldOrigin, error) { + owner := dereferenceType(typ) + for i, fieldIndex := range index { + if owner.Kind() != reflect.Struct || fieldIndex >= owner.NumField() { + return fieldOrigin{}, fmt.Errorf("invalid field index %v for %s", index, typ) } - if _, exists := versions[version][kind]; exists { - panic("duplicate version and kind: " + version + " " + kind) + field := owner.Field(fieldIndex) + if i == len(index)-1 { + return fieldOrigin{owner: owner, field: field}, nil } - versions[version][kind] = doc + owner = dereferenceType(field.Type) } + return fieldOrigin{}, fmt.Errorf("empty field index for %s", typ) +} - enc := json.NewEncoder(os.Stdout) - enc.SetIndent("", " ") - if err := enc.Encode(versions); err != nil { - panic(err) +func dereferenceType(typ reflect.Type) reflect.Type { + for typ.Kind() == reflect.Pointer { + typ = typ.Elem() + } + return typ +} + +func newFieldDocResolver(packagePaths []string) (*fieldDocResolver, error) { + resolver := &fieldDocResolver{ + docs: make(map[fieldDocKey]string), + } + if len(packagePaths) == 0 { + return resolver, nil + } + + loaded, err := packages.Load(&packages.Config{ + Mode: packages.NeedName | + packages.NeedFiles | + packages.NeedCompiledGoFiles | + packages.NeedImports | + packages.NeedDeps | + packages.NeedTypes | + packages.NeedSyntax, + }, packagePaths...) + if err != nil { + return nil, fmt.Errorf("load packages for field documentation: %w", err) + } + + var packageErrors []error + for _, pkg := range loaded { + for _, pkgErr := range pkg.Errors { + packageErrors = append(packageErrors, fmt.Errorf("package %s: %w", pkg.PkgPath, pkgErr)) + } + } + if len(packageErrors) > 0 { + return nil, fmt.Errorf("load packages for field documentation: %w", errors.Join(packageErrors...)) + } + + for _, pkg := range loaded { + resolver.indexPackage(pkg) + } + return resolver, nil +} + +func (r *fieldDocResolver) indexPackage(pkg *packages.Package) { + parser := newCommentParser(pkg) + printer := comment.Printer{ + DocLinkURL: func(link *comment.DocLink) string { + if link.ImportPath == "" { + link.ImportPath = pkg.PkgPath + } + return link.DefaultURL(docLinkBaseURL) + }, + } + for _, file := range pkg.Syntax { + for _, declaration := range file.Decls { + general, ok := declaration.(*ast.GenDecl) + if !ok { + continue + } + for _, spec := range general.Specs { + typeSpec, ok := spec.(*ast.TypeSpec) + if !ok { + continue + } + r.indexTypeFields(pkg.PkgPath, parser, &printer, typeSpec) + } + } + } +} + +func (r *fieldDocResolver) indexTypeFields( + packagePath string, + parser *comment.Parser, + printer *comment.Printer, + typeSpec *ast.TypeSpec, +) { + structType, ok := typeSpec.Type.(*ast.StructType) + if !ok { + return + } + for _, field := range structType.Fields.List { + var doc string + if field.Doc != nil { + doc = strings.TrimSpace(string(printer.Markdown(parser.Parse(field.Doc.Text())))) + } + for _, name := range astFieldNames(field) { + r.docs[fieldDocKey{ + packagePath: packagePath, + typeName: typeSpec.Name.Name, + fieldName: name, + }] = doc + } + } +} + +func newCommentParser(current *packages.Package) *comment.Parser { + return &comment.Parser{ + LookupPackage: func(name string) (string, bool) { + for path, imported := range current.Imports { + if imported.Name == name { + return path, true + } + } + return "", false + }, + LookupSym: func(recv, name string) bool { + if recv == "" { + return current.Types.Scope().Lookup(name) != nil + } + object := current.Types.Scope().Lookup(recv) + if object == nil { + return false + } + member, _, _ := types.LookupFieldOrMethod(object.Type(), true, current.Types, name) + return member != nil + }, + } +} + +func astFieldNames(field *ast.Field) []string { + if len(field.Names) > 0 { + names := make([]string, len(field.Names)) + for i := range field.Names { + names[i] = field.Names[i].Name + } + return names + } + if name := embeddedFieldName(field.Type); name != "" { + return []string{name} + } + return nil +} + +func embeddedFieldName(expression ast.Expr) string { + switch expression := expression.(type) { + case *ast.Ident: + return expression.Name + case *ast.SelectorExpr: + return expression.Sel.Name + case *ast.StarExpr: + return embeddedFieldName(expression.X) + case *ast.IndexExpr: + return embeddedFieldName(expression.X) + case *ast.IndexListExpr: + return embeddedFieldName(expression.X) + case *ast.ParenExpr: + return embeddedFieldName(expression.X) + default: + return "" + } +} + +func recoverFieldDocs( + doc *govydoc.ObjectDoc, + origins map[string]fieldOrigin, + resolver *fieldDocResolver, +) error { + var recoveryErrors []error + for i := range doc.Properties { + property := &doc.Properties[i] + if property.FieldDoc != "" { + continue + } + path := property.Path.String() + origin, ok := origins[path] + if !ok { + if isRootOrSyntheticWildcardPath(path, origins) { + continue + } + recoveryErrors = append(recoveryErrors, fmt.Errorf( + "recover field documentation for %s at %s: path has no Go field origin", + doc.Name, + property.Path, + )) + continue + } + fieldDoc, indexed := resolver.docs[fieldDocKey{ + packagePath: origin.owner.PkgPath(), + typeName: origin.owner.Name(), + fieldName: origin.field.Name, + }] + if !indexed { + recoveryErrors = append(recoveryErrors, fmt.Errorf( + "recover field documentation for %s at %s: %s.%s is missing from the AST field index", + doc.Name, + property.Path, + origin.owner, + origin.field.Name, + )) + continue + } + if fieldDoc == "" { + continue + } + property.FieldDoc = fieldDoc + if match := deprecatedDocRegex.FindStringSubmatch(fieldDoc); len(match) > 1 { + if property.DeprecatedDoc == "" { + property.DeprecatedDoc = strings.TrimSpace(match[1]) + } + property.FieldDoc = strings.TrimSpace(deprecatedDocRegex.ReplaceAllString(fieldDoc, "")) + } + } + return errors.Join(recoveryErrors...) +} + +func isRootOrSyntheticWildcardPath(path string, origins map[string]fieldOrigin) bool { + if path == jsonpath.NewRoot().String() { + return true + } + for { + parent, ok := cutWildcardSuffix(path) + if !ok { + return false + } + if _, ok = origins[parent]; ok { + return true + } + path = parent + } +} + +func cutWildcardSuffix(path string) (string, bool) { + for _, suffix := range [...]string{"[*]", ".*", ".*~"} { + if parent, ok := strings.CutSuffix(path, suffix); ok { + return parent, true + } } + return "", false } diff --git a/internal/cmd/objectdoc/main_test.go b/internal/cmd/objectdoc/main_test.go new file mode 100644 index 0000000..2810a1d --- /dev/null +++ b/internal/cmd/objectdoc/main_test.go @@ -0,0 +1,504 @@ +package main + +import ( + "bytes" + "errors" + "maps" + "os" + "reflect" + "slices" + "testing" + + "github.com/nieomylnieja/govydoc/pkg/govydoc" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/nobl9/govy/pkg/govy" + "github.com/nobl9/govy/pkg/jsonpath" + + v1 "github.com/OpenSLO/go-sdk/pkg/openslo/v1" + "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" +) + +func TestGenerateObjectDocRequiresPredicateDescriptions(t *testing.T) { + validator := govy.New[v1.Service]().When(func(v1.Service) bool { return true }) + + _, err := generateObjectDoc(validator) + + require.ErrorContains(t, err, "predicates without description found at: validator level") +} + +func TestGenerateObjectDocRejectsBlankRuleDescriptions(t *testing.T) { + validator := govy.New( + govy.For(govy.GetSelf[v1.Service]()).Rules( + govy.NewRule(func(v1.Service) error { return nil }).WithDescription(" \t"), + ), + ) + + _, err := generateObjectDoc(validator) + + require.ErrorContains(t, err, "validation rule 1 for $ in v1.Service has a blank description") +} + +func TestRegisteredValidatorsHaveCompletePlans(t *testing.T) { + for _, generator := range allDocsGenerators { + t.Run(generator.name, func(t *testing.T) { + _, err := generator.generate() + require.NoError(t, err) + }) + } +} + +func TestGenerateObjectDocsReturnsAllErrorsInGeneratorOrder(t *testing.T) { + firstErr := errors.New("first failure") + secondErr := errors.New("second failure") + secondFinished := make(chan struct{}) + generators := []objectDocGenerator{ + { + name: "first", + generate: func() (generatedObjectDoc, error) { + <-secondFinished + return generatedObjectDoc{}, firstErr + }, + }, + { + name: "second", + generate: func() (generatedObjectDoc, error) { + close(secondFinished) + return generatedObjectDoc{}, secondErr + }, + }, + } + + _, err := generateObjectDocs(generators) + + require.Error(t, err) + assert.ErrorIs(t, err, firstErr) + assert.ErrorIs(t, err, secondErr) + assert.EqualError( + t, + err, + "generate first documentation: first failure\ngenerate second documentation: second failure", + ) +} + +func TestAggregateVersionsRequiresOneDiscriminatorValue(t *testing.T) { + tests := map[string]struct { + versionValues []string + kindValues []string + expectedError string + }{ + "apiVersion without values": { + kindValues: []string{"Service"}, + expectedError: `document "Example" discriminator property $.apiVersion must have exactly one value, but it has 0`, + }, + "kind with multiple values": { + versionValues: []string{"openslo/v1"}, + kindValues: []string{"Service", "SLO"}, + expectedError: `document "Example" discriminator property $.kind must have exactly one value, but it has 2`, + }, + "kind with empty value": { + versionValues: []string{"openslo/v1"}, + kindValues: []string{""}, + expectedError: `document "Example" discriminator property $.kind has an empty value`, + }, + } + for name, test := range tests { + t.Run(name, func(t *testing.T) { + _, err := aggregateVersions([]generatedObjectDoc{ + newGeneratedObjectDoc("Example", test.versionValues, test.kindValues), + }) + + require.EqualError(t, err, test.expectedError) + }) + } +} + +func TestAggregateVersionsRejectsDuplicateAndMissingDiscriminatorPaths(t *testing.T) { + versionProperty := newDiscriminatorProperty(apiVersionPath, "openslo/v1") + kindProperty := newDiscriminatorProperty(kindPath, "Service") + tests := map[string]struct { + properties []govydoc.PropertyDoc + expectedError string + }{ + "duplicate apiVersion": { + properties: []govydoc.PropertyDoc{versionProperty, versionProperty, kindProperty}, + expectedError: `document "Example" has duplicate discriminator property $.apiVersion`, + }, + "duplicate kind": { + properties: []govydoc.PropertyDoc{versionProperty, kindProperty, kindProperty}, + expectedError: `document "Example" has duplicate discriminator property $.kind`, + }, + "missing apiVersion": { + properties: []govydoc.PropertyDoc{kindProperty}, + expectedError: `document "Example" is missing discriminator property $.apiVersion`, + }, + "missing kind": { + properties: []govydoc.PropertyDoc{versionProperty}, + expectedError: `document "Example" is missing discriminator property $.kind`, + }, + } + for name, test := range tests { + t.Run(name, func(t *testing.T) { + _, err := aggregateVersions([]generatedObjectDoc{{ + doc: govydoc.ObjectDoc{Name: "Example", Properties: test.properties}, + }}) + + require.EqualError(t, err, test.expectedError) + }) + } +} + +func TestAggregateVersionsRejectsDuplicateVersionAndKind(t *testing.T) { + docs := []generatedObjectDoc{ + newGeneratedObjectDoc("First", []string{"openslo/v1"}, []string{"Service"}), + newGeneratedObjectDoc("Second", []string{"openslo/v1"}, []string{"Service"}), + } + + _, err := aggregateVersions(docs) + + require.ErrorContains(t, err, `duplicate version and kind "openslo/v1" "Service"`) + assert.ErrorContains(t, err, `document "Second"`) + assert.ErrorContains(t, err, `already used by "First"`) +} + +func TestNormalizeRawMessages(t *testing.T) { + rawMessagePath := jsonpath.Parse("$.spec.connectionDetails") + rawMessageWildcardPath := rawMessagePath.IndexWildcard() + byteSlicePath := jsonpath.Parse("$.spec.payload") + byteSliceWildcardPath := byteSlicePath.IndexWildcard() + doc := govydoc.ObjectDoc{Properties: []govydoc.PropertyDoc{ + { + PropertyPlan: govy.PropertyPlan{ + Path: rawMessagePath, + TypeInfo: govy.TypeInfo{ + Name: jsonRawMessageName, + Kind: jsonRawMessageKind, + Package: jsonRawMessagePkg, + }, + }, + ChildrenPaths: []string{rawMessageWildcardPath.String()}, + }, + { + PropertyPlan: govy.PropertyPlan{ + Path: rawMessageWildcardPath, + TypeInfo: govy.TypeInfo{Name: "uint8", Kind: "uint8"}, + }, + }, + { + PropertyPlan: govy.PropertyPlan{ + Path: byteSlicePath, + TypeInfo: govy.TypeInfo{Name: "", Kind: jsonRawMessageKind}, + }, + ChildrenPaths: []string{byteSliceWildcardPath.String()}, + }, + { + PropertyPlan: govy.PropertyPlan{ + Path: byteSliceWildcardPath, + TypeInfo: govy.TypeInfo{Name: "uint8", Kind: "uint8"}, + }, + }, + }} + + normalizeRawMessages(&doc) + + require.Len(t, doc.Properties, 3) + rawMessage := requireProperty(t, doc, rawMessagePath) + assert.Equal(t, jsonRawMessageName, rawMessage.TypeInfo.Name) + assert.Equal(t, jsonValueKind, rawMessage.TypeInfo.Kind) + assert.Equal(t, jsonRawMessagePkg, rawMessage.TypeInfo.Package) + assert.Empty(t, rawMessage.ChildrenPaths) + assert.Nil(t, findProperty(doc, rawMessageWildcardPath)) + assert.NotNil(t, findProperty(doc, byteSliceWildcardPath)) + assert.Equal(t, jsonRawMessageKind, requireProperty(t, doc, byteSlicePath).TypeInfo.Kind) +} + +func TestNormalizeGeneratedDocsRecoversPromotedFieldDocs(t *testing.T) { + typeDocumentation := "existing type documentation" + docs := []generatedObjectDoc{ + { + rootType: reflect.TypeFor[v1.AlertPolicy](), + doc: govydoc.ObjectDoc{ + Name: "v1.AlertPolicy", + Properties: []govydoc.PropertyDoc{ + newPropertyDoc("$.spec.conditions[*].conditionRef", typeDocumentation), + newPropertyDoc("$.spec.notificationTargets[*].targetRef", typeDocumentation), + }, + }, + }, + { + rootType: reflect.TypeFor[v1.SLO](), + doc: govydoc.ObjectDoc{ + Name: "v1.SLO", + Properties: []govydoc.PropertyDoc{ + newPropertyDoc("$.spec.alertPolicies[*].alertPolicyRef", typeDocumentation), + }, + }, + }, + { + rootType: reflect.TypeFor[v2alpha.AlertPolicy](), + doc: govydoc.ObjectDoc{ + Name: "v2alpha.AlertPolicy", + Properties: []govydoc.PropertyDoc{ + newPropertyDoc("$.spec.conditions[*].conditionRef", typeDocumentation), + newPropertyDoc("$.spec.notificationTargets[*].targetRef", typeDocumentation), + }, + }, + }, + { + rootType: reflect.TypeFor[v2alpha.SLO](), + doc: govydoc.ObjectDoc{ + Name: "v2alpha.SLO", + Properties: []govydoc.PropertyDoc{ + newPropertyDoc("$.spec.alertPolicies[*].alertPolicyRef", typeDocumentation), + }, + }, + }, + } + + require.NoError(t, normalizeGeneratedDocs(docs)) + + tests := []struct { + name string + doc govydoc.ObjectDoc + path string + fieldDoc string + }{ + { + name: "v1 conditionRef", + doc: docs[0].doc, + path: "$.spec.conditions[*].conditionRef", + fieldDoc: "ConditionRef names an existing alert condition.", + }, + { + name: "v1 targetRef", + doc: docs[0].doc, + path: "$.spec.notificationTargets[*].targetRef", + fieldDoc: "TargetRef names an existing notification target.", + }, + { + name: "v1 alertPolicyRef", + doc: docs[1].doc, + path: "$.spec.alertPolicies[*].alertPolicyRef", + fieldDoc: "AlertPolicyRef names an existing alert policy.", + }, + { + name: "v2alpha conditionRef", + doc: docs[2].doc, + path: "$.spec.conditions[*].conditionRef", + fieldDoc: "ConditionRef names the alert condition to use.", + }, + { + name: "v2alpha targetRef", + doc: docs[2].doc, + path: "$.spec.notificationTargets[*].targetRef", + fieldDoc: "TargetRef names the notification target to use.", + }, + { + name: "v2alpha alertPolicyRef", + doc: docs[3].doc, + path: "$.spec.alertPolicies[*].alertPolicyRef", + fieldDoc: "AlertPolicyRef names the alert policy to use.", + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + property := requireProperty(t, test.doc, jsonpath.Parse(test.path)) + assert.Equal(t, test.fieldDoc, property.FieldDoc) + assert.Equal(t, typeDocumentation, property.TypeDoc) + }) + } +} + +func TestRecoverFieldDocsReportsFieldsMissingFromASTIndex(t *testing.T) { + fixtureType := reflect.TypeFor[fieldDocRecoveryFixture]() + fields := []struct { + name string + path string + }{ + {name: "Documented", path: "$.documented"}, + {name: "Undocumented", path: "$.undocumented"}, + {name: "MissingFirst", path: "$.missingFirst"}, + {name: "MissingSecond", path: "$.missingSecond"}, + } + doc := govydoc.ObjectDoc{Name: "FixtureDoc"} + origins := make(map[string]fieldOrigin, len(fields)) + resolver := &fieldDocResolver{docs: make(map[fieldDocKey]string)} + for _, item := range fields { + field, ok := fixtureType.FieldByName(item.name) + require.True(t, ok) + doc.Properties = append(doc.Properties, newPropertyDoc(item.path, "")) + origins[item.path] = fieldOrigin{owner: fixtureType, field: field} + key := fieldDocKey{ + packagePath: fixtureType.PkgPath(), + typeName: fixtureType.Name(), + fieldName: field.Name, + } + switch item.name { + case "Documented": + resolver.docs[key] = "Recovered field documentation." + case "Undocumented": + resolver.docs[key] = "" + } + } + + err := recoverFieldDocs(&doc, origins, resolver) + + require.Error(t, err) + assert.ErrorContains(t, err, "recover field documentation for FixtureDoc at $.missingFirst") + assert.ErrorContains(t, err, "fieldDocRecoveryFixture.MissingFirst is missing from the AST field index") + assert.ErrorContains(t, err, "recover field documentation for FixtureDoc at $.missingSecond") + assert.ErrorContains(t, err, "fieldDocRecoveryFixture.MissingSecond is missing from the AST field index") + assert.Equal(t, "Recovered field documentation.", requireProperty(t, doc, jsonpath.Parse("$.documented")).FieldDoc) + assert.Empty(t, requireProperty(t, doc, jsonpath.Parse("$.undocumented")).FieldDoc) +} + +func TestRecoverFieldDocsRejectsOriginlessRealPaths(t *testing.T) { + fixtureType := reflect.TypeFor[fieldDocRecoveryFixture]() + field, ok := fixtureType.FieldByName("Documented") + require.True(t, ok) + origins := map[string]fieldOrigin{ + "$.labels": {owner: fixtureType, field: field}, + } + doc := govydoc.ObjectDoc{ + Name: "FixtureDoc", + Properties: []govydoc.PropertyDoc{ + newPropertyDoc("$", ""), + newPropertyDoc("$.labels[*]", ""), + newPropertyDoc("$.labels.*", ""), + newPropertyDoc("$.labels.*~", ""), + newPropertyDoc("$.labels.*[*]", ""), + newPropertyDoc("$.labels.*~[*]", ""), + newPropertyDoc("$.items[*].promoted", ""), + }, + } + resolver := &fieldDocResolver{docs: make(map[fieldDocKey]string)} + + err := recoverFieldDocs(&doc, origins, resolver) + + require.EqualError( + t, + err, + "recover field documentation for FixtureDoc at $.items[*].promoted: path has no Go field origin", + ) +} + +func TestGenerateVersionsMatchesCanonicalManifest(t *testing.T) { + first, err := generateVersions() + require.NoError(t, err) + second, err := generateVersions() + require.NoError(t, err) + + var firstOutput bytes.Buffer + require.NoError(t, encodeVersions(&firstOutput, first)) + var secondOutput bytes.Buffer + require.NoError(t, encodeVersions(&secondOutput, second)) + require.True( + t, + bytes.Equal(firstOutput.Bytes(), secondOutput.Bytes()), + "successive generations produced different serialized output", + ) + + expectedKinds := map[Version][]Kind{ + "openslo/v1alpha": {"SLO", "Service"}, + "openslo/v1": { + "AlertCondition", + "AlertNotificationTarget", + "AlertPolicy", + "DataSource", + "SLI", + "SLO", + "Service", + }, + "openslo.com/v2alpha": { + "AlertCondition", + "AlertNotificationTarget", + "AlertPolicy", + "DataSource", + "SLI", + "SLO", + "Service", + }, + } + assert.Equal(t, expectedKinds, collectVersionKinds(first)) + + connectionDetailsPath := jsonpath.Parse("$.spec.connectionDetails") + connectionDetailsWildcardPath := connectionDetailsPath.IndexWildcard() + for _, version := range []Version{"openslo/v1", "openslo.com/v2alpha"} { + documents, ok := first[version] + require.True(t, ok, "version %s not found", version) + dataSource, ok := documents["DataSource"] + require.True(t, ok, "DataSource not found for version %s", version) + connectionDetails := requireProperty(t, dataSource, connectionDetailsPath) + assert.Equal(t, jsonRawMessageName, connectionDetails.TypeInfo.Name) + assert.Equal(t, jsonValueKind, connectionDetails.TypeInfo.Kind) + assert.Equal(t, jsonRawMessagePkg, connectionDetails.TypeInfo.Package) + assert.NotContains(t, connectionDetails.ChildrenPaths, connectionDetailsWildcardPath.String()) + assert.Nil(t, findProperty(dataSource, connectionDetailsWildcardPath)) + } + + checkedIn, err := os.ReadFile("../../../docs/manifest.json") + require.NoError(t, err) + require.True( + t, + bytes.Equal(checkedIn, firstOutput.Bytes()), + "docs/manifest.json is stale: checked-in size %d, generated size %d. Run make generate", + len(checkedIn), + firstOutput.Len(), + ) +} + +type fieldDocRecoveryFixture struct { + Documented string `json:"documented"` + Undocumented string `json:"undocumented"` + MissingFirst string `json:"missingFirst"` + MissingSecond string `json:"missingSecond"` +} + +func newGeneratedObjectDoc(name string, versionValues, kindValues []string) generatedObjectDoc { + return generatedObjectDoc{doc: govydoc.ObjectDoc{ + Name: name, + Properties: []govydoc.PropertyDoc{ + newDiscriminatorProperty(apiVersionPath, versionValues...), + newDiscriminatorProperty(kindPath, kindValues...), + }, + }} +} + +func newDiscriminatorProperty(path jsonpath.Path, values ...string) govydoc.PropertyDoc { + return govydoc.PropertyDoc{PropertyPlan: govy.PropertyPlan{ + Path: path, + Values: values, + }} +} + +func collectVersionKinds(versions Versions) map[Version][]Kind { + kinds := make(map[Version][]Kind, len(versions)) + for version, documents := range versions { + kinds[version] = slices.Sorted(maps.Keys(documents)) + } + return kinds +} + +func newPropertyDoc(path, typeDoc string) govydoc.PropertyDoc { + return govydoc.PropertyDoc{ + PropertyPlan: govy.PropertyPlan{Path: jsonpath.Parse(path)}, + TypeDoc: typeDoc, + } +} + +func requireProperty(t *testing.T, doc govydoc.ObjectDoc, path jsonpath.Path) *govydoc.PropertyDoc { + t.Helper() + property := findProperty(doc, path) + require.NotNil(t, property, "property %s not found", path) + return property +} + +func findProperty(doc govydoc.ObjectDoc, path jsonpath.Path) *govydoc.PropertyDoc { + for i := range doc.Properties { + if doc.Properties[i].Path.Equal(path) { + return &doc.Properties[i] + } + } + return nil +} diff --git a/pkg/openslo/kind.go b/pkg/openslo/kind.go index 83da089..477c4f2 100644 --- a/pkg/openslo/kind.go +++ b/pkg/openslo/kind.go @@ -18,6 +18,7 @@ const ( KindAlertNotificationTarget Kind = "AlertNotificationTarget" ) +// ParseKind parses and validates an OpenSLO object kind. func ParseKind(s string) (Kind, error) { kind := Kind(s) if err := kind.Validate(); err != nil { @@ -26,10 +27,12 @@ func ParseKind(s string) (Kind, error) { return kind, nil } +// String returns the serialized object kind. func (k Kind) String() string { return string(k) } +// Validate reports whether k is a supported object kind. func (k Kind) Validate() error { switch k { case KindSLO, diff --git a/pkg/openslo/v1/alert_condition.go b/pkg/openslo/v1/alert_condition.go index fe85fc5..c3d0961 100644 --- a/pkg/openslo/v1/alert_condition.go +++ b/pkg/openslo/v1/alert_condition.go @@ -13,6 +13,7 @@ var ( _ = openslo.ObjectValidator[AlertCondition](AlertCondition{}) ) +// NewAlertCondition returns an AlertCondition from metadata and spec. func NewAlertCondition(metadata Metadata, spec AlertConditionSpec) AlertCondition { return AlertCondition{ APIVersion: APIVersion, @@ -22,6 +23,8 @@ func NewAlertCondition(metadata Metadata, spec AlertConditionSpec) AlertConditio } } +// AlertCondition defines the condition under which an SLO alert is considered +// breaching. type AlertCondition struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -29,51 +32,72 @@ type AlertCondition struct { Spec AlertConditionSpec `json:"spec"` } +// GetVersion returns [APIVersion]. func (a AlertCondition) GetVersion() openslo.Version { return APIVersion } +// GetKind returns [openslo.KindAlertCondition]. func (a AlertCondition) GetKind() openslo.Kind { return openslo.KindAlertCondition } +// GetName returns the name in the AlertCondition's [Metadata]. func (a AlertCondition) GetName() string { return a.Metadata.Name } +// Validate returns an error for an invalid alert condition. func (a AlertCondition) Validate() error { return alertConditionValidation.Validate(a) } +// String returns the AlertCondition's formatted version, kind, and name. func (a AlertCondition) String() string { return internal.GetObjectName(a) } +// GetMetadata returns the AlertCondition's [Metadata]. func (a AlertCondition) GetMetadata() Metadata { return a.Metadata } +// GetValidator returns the validator for AlertCondition objects. func (a AlertCondition) GetValidator() govy.Validator[AlertCondition] { return alertConditionValidation } +// AlertConditionSpec defines an alert's severity and burn-rate condition. type AlertConditionSpec struct { - Severity string `json:"severity"` - Condition AlertConditionType `json:"condition"` - Description string `json:"description,omitempty"` + // Severity is an implementation-defined classification such as "sev1" or "page". + Severity string `json:"severity"` + Condition AlertConditionType `json:"condition"` + // Description summarizes the alert condition. + Description string `json:"description,omitempty"` } +// AlertConditionType defines a comparison against an SLO's burn rate. Burn rate +// is error-budget consumption relative to the rate allowed by the SLO. type AlertConditionType struct { - Kind AlertConditionKind `json:"kind"` - Operator Operator `json:"op"` - Threshold *float64 `json:"threshold"` - LookbackWindow DurationShorthand `json:"lookbackWindow"` - AlertAfter *DurationShorthand `json:"alertAfter,omitempty"` + // Kind selects the condition calculation. OpenSLO defaults Kind to + // [AlertConditionKindBurnRate]. This SDK does not apply that default. + Kind AlertConditionKind `json:"kind"` + // Operator compares the calculated burn rate with Threshold. + Operator Operator `json:"op"` + // Threshold sets the numeric burn-rate boundary. + Threshold *float64 `json:"threshold"` + // LookbackWindow sets the period for burn-rate calculation. + LookbackWindow DurationShorthand `json:"lookbackWindow"` + // AlertAfter sets how long the comparison must hold before the condition + // breaches. OpenSLO treats an omitted value as "0m". This SDK leaves it unset. + AlertAfter *DurationShorthand `json:"alertAfter,omitempty"` } +// AlertConditionKind identifies the calculation used by an [AlertConditionType]. type AlertConditionKind string const ( + // AlertConditionKindBurnRate compares an SLO's error-budget burn rate. AlertConditionKindBurnRate AlertConditionKind = "burnrate" ) @@ -126,4 +150,7 @@ var alertConditionBurnRateValidation = govy.New( WithName("alertAfter"). Include(durationShortHandValidation), ). - When(func(a AlertConditionType) bool { return a.Kind == AlertConditionKindBurnRate }) + When( + func(a AlertConditionType) bool { return a.Kind == AlertConditionKindBurnRate }, + govy.WhenDescription("'kind' is 'burnrate'"), + ) diff --git a/pkg/openslo/v1/alert_condition_test.go b/pkg/openslo/v1/alert_condition_test.go index dfee861..9e7fa8e 100644 --- a/pkg/openslo/v1/alert_condition_test.go +++ b/pkg/openslo/v1/alert_condition_test.go @@ -1,9 +1,11 @@ package v1 import ( + "slices" "strings" "testing" + "github.com/nobl9/govy/pkg/govy" "github.com/nobl9/govy/pkg/govytest" "github.com/nobl9/govy/pkg/rules" @@ -53,6 +55,19 @@ func TestAlertCondition_Validate_Spec(t *testing.T) { }) } +func TestAlertCondition_ValidationPlan(t *testing.T) { + plan, err := govy.Plan(alertConditionValidation, govy.PlanStrictMode()) + assert.Require(t, assert.NoError(t, err)) + + assertValidationPlanRule( + t, + plan, + "$.spec.condition.op", + "must be one of: gt, lt, gte, lte", + "'kind' is 'burnrate'", + ) +} + func runAlertConditionSpecTests[T openslo.Object]( t *testing.T, path string, @@ -206,3 +221,31 @@ func validAlertCondition() AlertCondition { }, ) } + +func assertValidationPlanRule( + t *testing.T, + plan *govy.ValidatorPlan, + path string, + description string, + conditions ...string, +) { + t.Helper() + for _, property := range plan.Properties { + if property.Path.String() != path { + continue + } + for _, rule := range property.Rules { + if rule.Description != description { + continue + } + for _, condition := range conditions { + if !slices.Contains(rule.Conditions, condition) { + t.Errorf("validation rule %q at %s does not have condition %q", description, path, condition) + return + } + } + return + } + } + t.Errorf("validation plan does not contain rule %q at %s", description, path) +} diff --git a/pkg/openslo/v1/alert_notification_target.go b/pkg/openslo/v1/alert_notification_target.go index e67fb37..680c7a2 100644 --- a/pkg/openslo/v1/alert_notification_target.go +++ b/pkg/openslo/v1/alert_notification_target.go @@ -13,6 +13,7 @@ var ( _ = openslo.ObjectValidator[AlertNotificationTarget](AlertNotificationTarget{}) ) +// NewAlertNotificationTarget returns a notification target from metadata and spec. func NewAlertNotificationTarget(metadata Metadata, spec AlertNotificationTargetSpec) AlertNotificationTarget { return AlertNotificationTarget{ APIVersion: APIVersion, @@ -22,6 +23,7 @@ func NewAlertNotificationTarget(metadata Metadata, spec AlertNotificationTargetS } } +// AlertNotificationTarget identifies a destination for SLO alert notifications. type AlertNotificationTarget struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -29,37 +31,48 @@ type AlertNotificationTarget struct { Spec AlertNotificationTargetSpec `json:"spec"` } +// GetVersion returns [APIVersion]. func (a AlertNotificationTarget) GetVersion() openslo.Version { return APIVersion } +// GetKind returns [openslo.KindAlertNotificationTarget]. func (a AlertNotificationTarget) GetKind() openslo.Kind { return openslo.KindAlertNotificationTarget } +// GetName returns the name in the target's [Metadata]. func (a AlertNotificationTarget) GetName() string { return a.Metadata.Name } +// Validate returns an error for an invalid notification target. func (a AlertNotificationTarget) Validate() error { return alertNotificationTargetValidation.Validate(a) } +// String returns the target's formatted version, kind, and name. func (a AlertNotificationTarget) String() string { return internal.GetObjectName(a) } +// GetMetadata returns the target's [Metadata]. func (a AlertNotificationTarget) GetMetadata() Metadata { return a.Metadata } +// GetValidator returns the validator for AlertNotificationTarget objects. func (a AlertNotificationTarget) GetValidator() govy.Validator[AlertNotificationTarget] { return alertNotificationTargetValidation } +// AlertNotificationTargetSpec defines an implementation-specific notification +// destination. type AlertNotificationTargetSpec struct { + // Description summarizes the destination. Description string `json:"description,omitempty"` - Target string `json:"target"` + // Target identifies the implementation-defined delivery target. + Target string `json:"target"` } var alertNotificationTargetValidation = govy.New( diff --git a/pkg/openslo/v1/alert_policy.go b/pkg/openslo/v1/alert_policy.go index 7a53898..e16537b 100644 --- a/pkg/openslo/v1/alert_policy.go +++ b/pkg/openslo/v1/alert_policy.go @@ -13,6 +13,7 @@ var ( _ = openslo.ObjectValidator[AlertPolicy](AlertPolicy{}) ) +// NewAlertPolicy returns an AlertPolicy from metadata and spec. func NewAlertPolicy(metadata Metadata, spec AlertPolicySpec) AlertPolicy { return AlertPolicy{ APIVersion: APIVersion, @@ -22,6 +23,10 @@ func NewAlertPolicy(metadata Metadata, spec AlertPolicySpec) AlertPolicy { } } +// AlertPolicy defines when the system emits alerts for an SLO and where it sends +// them. +// It is distinct from an organization's policy for responding to error-budget +// consumption. type AlertPolicy struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -29,70 +34,99 @@ type AlertPolicy struct { Spec AlertPolicySpec `json:"spec"` } +// GetVersion returns [APIVersion]. func (a AlertPolicy) GetVersion() openslo.Version { return APIVersion } +// GetKind returns [openslo.KindAlertPolicy]. func (a AlertPolicy) GetKind() openslo.Kind { return openslo.KindAlertPolicy } +// GetName returns the name in the AlertPolicy's [Metadata]. func (a AlertPolicy) GetName() string { return a.Metadata.Name } +// Validate returns an error for an invalid alert policy. func (a AlertPolicy) Validate() error { return alertPolicyValidation.Validate(a) } +// String returns the AlertPolicy's formatted version, kind, and name. func (a AlertPolicy) String() string { return internal.GetObjectName(a) } +// GetMetadata returns the AlertPolicy's [Metadata]. func (a AlertPolicy) GetMetadata() Metadata { return a.Metadata } +// GetValidator returns the validator for AlertPolicy objects. func (a AlertPolicy) GetValidator() govy.Validator[AlertPolicy] { return alertPolicyValidation } +// AlertPolicySpec defines the events that emit notifications and the condition +// and destinations used by an [AlertPolicy]. type AlertPolicySpec struct { - Description string `json:"description,omitempty"` - AlertWhenNoData bool `json:"alertWhenNoData,omitempty"` - AlertWhenBreaching bool `json:"alertWhenBreaching,omitempty"` - AlertWhenResolved bool `json:"alertWhenResolved,omitempty"` - Conditions []AlertPolicyCondition `json:"conditions,omitempty"` + // Description summarizes the alert policy. + Description string `json:"description,omitempty"` + // AlertWhenNoData enables alerts when the associated SLO has no burn-rate + // value. + AlertWhenNoData bool `json:"alertWhenNoData,omitempty"` + // AlertWhenBreaching enables alerts when the condition starts breaching. + AlertWhenBreaching bool `json:"alertWhenBreaching,omitempty"` + // AlertWhenResolved enables alerts when the condition resolves. + AlertWhenResolved bool `json:"alertWhenResolved,omitempty"` + // Conditions contains the alert condition, inline or by reference. + Conditions []AlertPolicyCondition `json:"conditions,omitempty"` + // NotificationTargets lists notification destinations, inline or by reference. NotificationTargets []AlertPolicyNotificationTarget `json:"notificationTargets,omitempty"` } +// AlertPolicyCondition supplies an alert condition to an [AlertPolicySpec] by +// reference or inline definition. type AlertPolicyCondition struct { *AlertPolicyConditionRef *AlertPolicyConditionInline } +// AlertPolicyConditionInline is an [AlertCondition] embedded in an +// [AlertPolicy]. type AlertPolicyConditionInline struct { Kind openslo.Kind `json:"kind"` Metadata Metadata `json:"metadata"` Spec AlertConditionSpec `json:"spec"` } +// AlertPolicyConditionRef refers to an existing [AlertCondition]. type AlertPolicyConditionRef struct { + // ConditionRef names an existing alert condition. ConditionRef string `json:"conditionRef"` } +// AlertPolicyNotificationTarget supplies a notification target to an +// [AlertPolicySpec] by reference or inline definition. type AlertPolicyNotificationTarget struct { *AlertPolicyNotificationTargetRef *AlertPolicyNotificationTargetInline } +// AlertPolicyNotificationTargetInline is an [AlertNotificationTarget] embedded +// in an [AlertPolicy]. type AlertPolicyNotificationTargetInline struct { Kind openslo.Kind `json:"kind"` Metadata Metadata `json:"metadata"` Spec AlertNotificationTargetSpec `json:"spec"` } +// AlertPolicyNotificationTargetRef refers to an existing +// [AlertNotificationTarget]. type AlertPolicyNotificationTargetRef struct { + // TargetRef names an existing notification target. TargetRef string `json:"targetRef"` } @@ -126,7 +160,8 @@ var alertPolicyConditionValidation = govy.New( // It's impossible to list all fields that constitute the inlined version in the error message, // therefore 'spec' must suffice. "spec": func(a AlertPolicyCondition) any { return a.AlertPolicyConditionInline }, - })), + }). + WithDescription("exactly one of 'conditionRef' and 'spec' must be set")), govy.ForPointer(func(a AlertPolicyCondition) *AlertPolicyConditionRef { return a.AlertPolicyConditionRef }). Include(govy.New( govy.For(func(ref AlertPolicyConditionRef) string { return ref.ConditionRef }). @@ -155,7 +190,8 @@ var alertPolicyNotificationTargetValidation = govy.New( // It's impossible to list all fields that constitute the inlined version in the error message, // therefore 'spec' must suffice. "spec": func(a AlertPolicyNotificationTarget) any { return a.AlertPolicyNotificationTargetInline }, - })), + }). + WithDescription("exactly one of 'targetRef' and 'spec' must be set")), govy.ForPointer(func(a AlertPolicyNotificationTarget) *AlertPolicyNotificationTargetRef { return a.AlertPolicyNotificationTargetRef }). diff --git a/pkg/openslo/v1/alert_policy_test.go b/pkg/openslo/v1/alert_policy_test.go index 3ec22c4..6b04931 100644 --- a/pkg/openslo/v1/alert_policy_test.go +++ b/pkg/openslo/v1/alert_policy_test.go @@ -5,6 +5,7 @@ import ( "strings" "testing" + "github.com/nobl9/govy/pkg/govy" "github.com/nobl9/govy/pkg/govytest" "github.com/nobl9/govy/pkg/rules" @@ -59,6 +60,27 @@ func TestAlertPolicy_Validate_Spec(t *testing.T) { }) } +func TestAlertPolicy_ValidationPlan(t *testing.T) { + plan, err := govy.Plan(alertPolicyValidation, govy.PlanStrictMode()) + assert.Require(t, assert.NoError(t, err)) + + for _, test := range []struct { + path string + description string + }{ + { + path: "$.spec.conditions[*]", + description: "exactly one of 'conditionRef' and 'spec' must be set", + }, + { + path: "$.spec.notificationTargets[*]", + description: "exactly one of 'targetRef' and 'spec' must be set", + }, + } { + assertValidationPlanRule(t, plan, test.path, test.description) + } +} + func runAlertPolicySpecTests[T openslo.Object]( t *testing.T, path string, diff --git a/pkg/openslo/v1/data_source.go b/pkg/openslo/v1/data_source.go index 4bd41a7..959898d 100644 --- a/pkg/openslo/v1/data_source.go +++ b/pkg/openslo/v1/data_source.go @@ -15,6 +15,7 @@ var ( _ = openslo.ObjectValidator[DataSource](DataSource{}) ) +// NewDataSource returns a data source from metadata and spec. func NewDataSource(metadata Metadata, spec DataSourceSpec) DataSource { return DataSource{ APIVersion: APIVersion, @@ -24,6 +25,7 @@ func NewDataSource(metadata Metadata, spec DataSourceSpec) DataSource { } } +// DataSource stores reusable connection details for a metrics backend. type DataSource struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -31,37 +33,48 @@ type DataSource struct { Spec DataSourceSpec `json:"spec"` } +// GetVersion returns [APIVersion]. func (d DataSource) GetVersion() openslo.Version { return APIVersion } +// GetKind returns [openslo.KindDataSource]. func (d DataSource) GetKind() openslo.Kind { return openslo.KindDataSource } +// GetName returns the name in the data source's [Metadata]. func (d DataSource) GetName() string { return d.Metadata.Name } +// Validate returns an error for an invalid data source. func (d DataSource) Validate() error { return dataSourceValidation.Validate(d) } +// String returns the data source's formatted version, kind, and name. func (d DataSource) String() string { return internal.GetObjectName(d) } +// GetMetadata returns the data source's [Metadata]. func (d DataSource) GetMetadata() Metadata { return d.Metadata } +// GetValidator returns the validator for DataSource objects. func (d DataSource) GetValidator() govy.Validator[DataSource] { return dataSourceValidation } +// DataSourceSpec defines reusable, source-specific connection configuration. type DataSourceSpec struct { - Description string `json:"description,omitempty"` - Type string `json:"type"` + // Description summarizes the data source. + Description string `json:"description,omitempty"` + // Type names the implementation-defined metric source. + Type string `json:"type"` + // ConnectionDetails stores source-specific connection configuration as JSON. ConnectionDetails json.RawMessage `json:"connectionDetails"` } diff --git a/pkg/openslo/v1/doc.go b/pkg/openslo/v1/doc.go index 3f9f21c..2a8eda5 100644 --- a/pkg/openslo/v1/doc.go +++ b/pkg/openslo/v1/doc.go @@ -1,2 +1,2 @@ -// Package v1 contains the OpenSLO specification version v1alpha definitions. +// Package v1 declares Go representations and validators for OpenSLO v1 objects. package v1 diff --git a/pkg/openslo/v1/duration_shorthand.go b/pkg/openslo/v1/duration_shorthand.go index 3024388..d342e0c 100644 --- a/pkg/openslo/v1/duration_shorthand.go +++ b/pkg/openslo/v1/duration_shorthand.go @@ -23,8 +23,10 @@ func NewDurationShorthand(value int, unit DurationShorthandUnit) DurationShortha } } -// DurationShorthand is a shorthand representation of time duration. -// It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days). +// DurationShorthand represents a duration as an integer and a case-sensitive +// [DurationShorthandUnit], such as "1m" or "10d". This SDK +// accepts zero and encodes it as empty text. OpenSLO specifies a positive +// integer. type DurationShorthand struct { unit DurationShorthandUnit value int @@ -34,7 +36,7 @@ type DurationShorthand struct { // Example: // // duration, _ := ParseDurationShorthand("1w") -// duration.GetUnit() -> "w" +// duration.GetUnit() // "w" func (d *DurationShorthand) GetUnit() DurationShorthandUnit { return d.unit } @@ -43,7 +45,7 @@ func (d *DurationShorthand) GetUnit() DurationShorthandUnit { // Example: // // duration, _ := ParseDurationShorthand("12w") -// duration.GetValue() -> "12" +// duration.GetValue() // 12 func (d *DurationShorthand) GetValue() int { return d.value } @@ -97,17 +99,25 @@ func (d DurationShorthand) Duration() time.Duration { } } -// DurationShorthandUnit is a unit of [DurationShorthand]. +// DurationShorthandUnit identifies the case-sensitive unit suffix of a +// [DurationShorthand]. type DurationShorthandUnit string const ( - DurationShorthandUnitMinute DurationShorthandUnit = "m" - DurationShorthandUnitHour DurationShorthandUnit = "h" - DurationShorthandUnitDay DurationShorthandUnit = "d" - DurationShorthandUnitWeek DurationShorthandUnit = "w" - DurationShorthandUnitMonth DurationShorthandUnit = "M" + // DurationShorthandUnitMinute represents minutes. + DurationShorthandUnitMinute DurationShorthandUnit = "m" + // DurationShorthandUnitHour represents hours. + DurationShorthandUnitHour DurationShorthandUnit = "h" + // DurationShorthandUnitDay represents days. + DurationShorthandUnitDay DurationShorthandUnit = "d" + // DurationShorthandUnitWeek represents weeks. + DurationShorthandUnitWeek DurationShorthandUnit = "w" + // DurationShorthandUnitMonth represents months. + DurationShorthandUnitMonth DurationShorthandUnit = "M" + // DurationShorthandUnitQuarter represents quarters. DurationShorthandUnitQuarter DurationShorthandUnit = "Q" - DurationShorthandUnitYear DurationShorthandUnit = "Y" + // DurationShorthandUnitYear represents years. + DurationShorthandUnitYear DurationShorthandUnit = "Y" ) var validDurationUnits = []DurationShorthandUnit{ @@ -120,7 +130,7 @@ var validDurationUnits = []DurationShorthandUnit{ DurationShorthandUnitYear, } -// Validate checks if [DurationShorthand] is correct. +// Validate returns an error for an invalid duration shorthand. func (d DurationShorthand) Validate() error { return durationShortHandValidation.Validate(d) } diff --git a/pkg/openslo/v1/objects.go b/pkg/openslo/v1/objects.go index e07ce31..5e8ae90 100644 --- a/pkg/openslo/v1/objects.go +++ b/pkg/openslo/v1/objects.go @@ -11,6 +11,7 @@ import ( "github.com/OpenSLO/go-sdk/pkg/openslo" ) +// APIVersion is the OpenSLO v1 API version. const APIVersion = openslo.VersionV1 var supportedKinds = []openslo.Kind{ @@ -23,28 +24,40 @@ var supportedKinds = []openslo.Kind{ openslo.KindAlertNotificationTarget, } +// GetSupportedKinds returns a copy of the object kinds supported by this package. func GetSupportedKinds() []openslo.Kind { return slices.Clone(supportedKinds) } +// Object is an OpenSLO v1 object with accessible [Metadata]. type Object interface { openslo.Object GetMetadata() Metadata } +// Metadata identifies and describes an OpenSLO v1 object. type Metadata struct { - Name string `json:"name"` - DisplayName string `json:"displayName,omitempty"` - Labels Labels `json:"labels,omitempty"` + // Name identifies the object. + Name string `json:"name"` + // DisplayName is the object's human-readable name. + DisplayName string `json:"displayName,omitempty"` + // Labels contains user-defined metadata for describing and grouping the object. + Labels Labels `json:"labels,omitempty"` + // Annotations contains implementation- or system-specific metadata. Annotations Annotations `json:"annotations,omitempty"` } +// Labels maps metadata keys to one or more values. type Labels map[string]Label +// Annotations maps metadata keys to implementation- or system-specific values. type Annotations map[string]string +// Label is the set of values assigned to one label key. +// JSON decoding accepts either a single string or an array of strings. type Label []string +// UnmarshalJSON decodes a string or string array into a Label. func (a *Label) UnmarshalJSON(data []byte) error { var multi []string if err := json.Unmarshal(data, &multi); err != nil { @@ -59,12 +72,17 @@ func (a *Label) UnmarshalJSON(data []byte) error { return nil } +// Operator identifies a comparison applied to a metric value. type Operator string const ( - OperatorGT Operator = "gt" - OperatorLT Operator = "lt" + // OperatorGT selects greater than. + OperatorGT Operator = "gt" + // OperatorLT selects less than. + OperatorLT Operator = "lt" + // OperatorGTE selects greater than or equal to. OperatorGTE Operator = "gte" + // OperatorLTE selects less than or equal to. OperatorLTE Operator = "lte" ) @@ -80,6 +98,7 @@ var operatorValidation = govy.New( Rules(rules.OneOf(validOperators...)), ) +// Validate returns an error for an unsupported operator. func (o Operator) Validate() error { return operatorValidation.Validate(o) } diff --git a/pkg/openslo/v1/service.go b/pkg/openslo/v1/service.go index 3e74ec6..2b2c4f2 100644 --- a/pkg/openslo/v1/service.go +++ b/pkg/openslo/v1/service.go @@ -13,6 +13,7 @@ var ( _ = openslo.ObjectValidator[Service](Service{}) ) +// NewService returns a service from metadata and spec. func NewService(metadata Metadata, spec ServiceSpec) Service { return Service{ APIVersion: APIVersion, @@ -22,6 +23,7 @@ func NewService(metadata Metadata, spec ServiceSpec) Service { } } +// Service groups related [SLO] objects. type Service struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -29,35 +31,44 @@ type Service struct { Spec ServiceSpec `json:"spec"` } +// GetVersion returns [APIVersion]. func (s Service) GetVersion() openslo.Version { return APIVersion } +// GetKind returns [openslo.KindService]. func (s Service) GetKind() openslo.Kind { return openslo.KindService } +// GetName returns the name in the Service's [Metadata]. func (s Service) GetName() string { return s.Metadata.Name } +// Validate returns an error for an invalid service. func (s Service) Validate() error { return serviceValidation.Validate(s) } +// String returns the Service's formatted version, kind, and name. func (s Service) String() string { return internal.GetObjectName(s) } +// GetMetadata returns the Service's [Metadata]. func (s Service) GetMetadata() Metadata { return s.Metadata } +// GetValidator returns the validator for Service objects. func (s Service) GetValidator() govy.Validator[Service] { return serviceValidation } +// ServiceSpec contains the descriptive properties of a [Service]. type ServiceSpec struct { + // Description summarizes the service. Description string `json:"description,omitempty"` } diff --git a/pkg/openslo/v1/sli.go b/pkg/openslo/v1/sli.go index 7fffa39..6136ffb 100644 --- a/pkg/openslo/v1/sli.go +++ b/pkg/openslo/v1/sli.go @@ -13,6 +13,7 @@ var ( _ = openslo.ObjectValidator[SLI](SLI{}) ) +// NewSLI returns an SLI from metadata and spec. func NewSLI(metadata Metadata, spec SLISpec) SLI { return SLI{ APIVersion: APIVersion, @@ -22,6 +23,7 @@ func NewSLI(metadata Metadata, spec SLISpec) SLI { } } +// SLI describes how to read metric data used to evaluate an [SLO]. type SLI struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -29,63 +31,97 @@ type SLI struct { Spec SLISpec `json:"spec"` } +// GetVersion returns [APIVersion]. func (s SLI) GetVersion() openslo.Version { return APIVersion } +// GetKind returns [openslo.KindSLI]. func (s SLI) GetKind() openslo.Kind { return openslo.KindSLI } +// GetName returns the name in the SLI's [Metadata]. func (s SLI) GetName() string { return s.Metadata.Name } +// Validate returns an error for an invalid SLI. func (s SLI) Validate() error { return sliValidation.Validate(s) } +// String returns the SLI's formatted version, kind, and name. func (s SLI) String() string { return internal.GetObjectName(s) } +// GetMetadata returns the SLI's [Metadata]. func (s SLI) GetMetadata() Metadata { return s.Metadata } +// GetValidator returns the validator for SLI objects. func (s SLI) GetValidator() govy.Validator[SLI] { return sliValidation } +// SLISpec defines the metric used by an [SLI]. type SLISpec struct { - Description string `json:"description,omitempty"` + // Description summarizes the SLI. + Description string `json:"description,omitempty"` + // ThresholdMetric retrieves raw values for an [SLOObjective] to compare with + // its Operator and Value. ThresholdMetric *SLIMetricSpec `json:"thresholdMetric,omitempty"` RatioMetric *SLIRatioMetric `json:"ratioMetric,omitempty"` } +// SLIRatioMetric defines a success ratio as good divided by total, total minus +// bad divided by total, or a precomputed raw ratio. For example, 99 good events +// out of 100 produce a ratio of 0.99. One bad event out of 100 produces the same +// ratio. type SLIRatioMetric struct { - Counter bool `json:"counter"` - Good *SLIMetricSpec `json:"good,omitempty"` - Bad *SLIMetricSpec `json:"bad,omitempty"` - Total *SLIMetricSpec `json:"total,omitempty"` + // Counter reports whether the queried good, bad, and total metrics are + // monotonically increasing. It has no effect when Raw is used. + Counter bool `json:"counter"` + // Good supplies the numerator for a good-over-total ratio. + Good *SLIMetricSpec `json:"good,omitempty"` + // Bad supplies the number subtracted from Total for a failure-based ratio. + Bad *SLIMetricSpec `json:"bad,omitempty"` + // Total supplies the denominator for a Good- or Bad-based ratio. + Total *SLIMetricSpec `json:"total,omitempty"` + // RawType selects whether Raw is interpreted as a success or failure ratio + // when Raw is used. RawType SLIRawMetricType `json:"rawType,omitempty"` - Raw *SLIMetricSpec `json:"raw,omitempty"` + // Raw retrieves a precomputed ratio. + Raw *SLIMetricSpec `json:"raw,omitempty"` } +// SLIMetricSpec defines how to retrieve one metric used by an [SLI]. type SLIMetricSpec struct { MetricSource SLIMetricSource `json:"metricSource"` } +// SLIMetricSource identifies a metrics backend and supplies the configuration +// needed to retrieve a metric. type SLIMetricSource struct { - MetricSourceRef string `json:"metricSourceRef,omitempty"` - Type string `json:"type,omitempty"` - Spec map[string]any `json:"spec"` + // MetricSourceRef names an existing [DataSource]. + MetricSourceRef string `json:"metricSourceRef,omitempty"` + // Type identifies the implementation-defined metric-source type. + // OpenSLO requires it when MetricSourceRef does not supply one. This SDK does + // not validate that condition. + Type string `json:"type,omitempty"` + // Spec contains source-specific query or metric-retrieval configuration. + Spec map[string]any `json:"spec"` } +// SLIRawMetricType identifies how a precomputed raw ratio is interpreted. type SLIRawMetricType string const ( + // SLIRawMetricTypeSuccess interprets Raw as good divided by total. SLIRawMetricTypeSuccess SLIRawMetricType = "success" + // SLIRawMetricTypeFailure interprets Raw as bad divided by total. SLIRawMetricTypeFailure SLIRawMetricType = "failure" ) @@ -111,7 +147,8 @@ var sliSpecValidation = govy.New( Rules(rules.MutuallyExclusive(true, map[string]func(s SLISpec) any{ "thresholdMetric": func(s SLISpec) any { return s.ThresholdMetric }, "ratioMetric": func(s SLISpec) any { return s.RatioMetric }, - })), + }). + WithDescription("exactly one of 'thresholdMetric' and 'ratioMetric' must be set")), govy.ForPointer(func(spec SLISpec) *SLIMetricSpec { return spec.ThresholdMetric }). WithName("thresholdMetric"). Include(sliMetricSpecValidation), @@ -126,7 +163,8 @@ var sliRatioMetricValidation = govy.New( Rules(rules.MutuallyExclusive(true, map[string]func(m SLIRatioMetric) any{ "total": func(m SLIRatioMetric) any { return m.Total }, "raw": func(m SLIRatioMetric) any { return m.Raw }, - })). + }). + WithDescription("exactly one of 'total' and 'raw' must be set")). Rules(rules.MutuallyExclusive(false, map[string]func(m SLIRatioMetric) any{ "raw": func(m SLIRatioMetric) any { return m.Raw }, "good": func(m SLIRatioMetric) any { return m.Good }, @@ -149,16 +187,25 @@ var sliFractionMetricValidation = govy.New( govy.ForPointer(func(m SLIRatioMetric) *SLIMetricSpec { return m.Good }). WithName("good"). Cascade(govy.CascadeModeContinue). - When(func(m SLIRatioMetric) bool { return m.Good != nil }). + When( + func(m SLIRatioMetric) bool { return m.Good != nil }, + govy.WhenDescription("'good' is set"), + ). Include(sliMetricSpecValidation), govy.ForPointer(func(m SLIRatioMetric) *SLIMetricSpec { return m.Bad }). WithName("bad"). Cascade(govy.CascadeModeContinue). - When(func(m SLIRatioMetric) bool { return m.Bad != nil }). + When( + func(m SLIRatioMetric) bool { return m.Bad != nil }, + govy.WhenDescription("'bad' is set"), + ). Include(sliMetricSpecValidation), ). Cascade(govy.CascadeModeStop). - When(func(m SLIRatioMetric) bool { return m.Total != nil }) + When( + func(m SLIRatioMetric) bool { return m.Total != nil }, + govy.WhenDescription("'total' is set"), + ) var sliRawMetricSpecValidation = govy.New( govy.ForPointer(func(m SLIRatioMetric) *SLIMetricSpec { return m.Raw }). @@ -169,7 +216,10 @@ var sliRawMetricSpecValidation = govy.New( Required(). Rules(rules.OneOf(validSLIRawMetricTypes...)), ). - When(func(m SLIRatioMetric) bool { return m.Raw != nil }) + When( + func(m SLIRatioMetric) bool { return m.Raw != nil }, + govy.WhenDescription("'raw' is set"), + ) var sliMetricSpecValidation = govy.New( govy.For(func(spec SLIMetricSpec) SLIMetricSource { return spec.MetricSource }). diff --git a/pkg/openslo/v1/sli_test.go b/pkg/openslo/v1/sli_test.go index 142f007..96b6784 100644 --- a/pkg/openslo/v1/sli_test.go +++ b/pkg/openslo/v1/sli_test.go @@ -62,6 +62,38 @@ func TestSLI_Validate_Spec(t *testing.T) { }) } +func TestSLI_ValidationPlan(t *testing.T) { + plan, err := govy.Plan(sliValidation, govy.PlanStrictMode()) + assert.Require(t, assert.NoError(t, err)) + + for _, test := range []struct { + path string + description string + }{ + { + path: "$.spec", + description: "exactly one of 'thresholdMetric' and 'ratioMetric' must be set", + }, + { + path: "$.spec.ratioMetric", + description: "exactly one of 'total' and 'raw' must be set", + }, + } { + assertValidationPlanRule(t, plan, test.path, test.description) + } + for _, test := range []struct { + path string + conditions []string + }{ + {path: "$.spec.ratioMetric.good", conditions: []string{"'total' is set", "'good' is set"}}, + {path: "$.spec.ratioMetric.bad", conditions: []string{"'total' is set", "'bad' is set"}}, + {path: "$.spec.ratioMetric.total", conditions: []string{"'total' is set"}}, + {path: "$.spec.ratioMetric.raw", conditions: []string{"'raw' is set"}}, + } { + assertValidationPlanRule(t, plan, test.path, "property is optional", test.conditions...) + } +} + func runSLISpecTests[T openslo.Object](t *testing.T, path string, objectGetter func(SLISpec) T) { t.Helper() diff --git a/pkg/openslo/v1/slo.go b/pkg/openslo/v1/slo.go index 9ee6dec..752b8df 100644 --- a/pkg/openslo/v1/slo.go +++ b/pkg/openslo/v1/slo.go @@ -16,6 +16,7 @@ var ( _ = openslo.ObjectValidator[SLO](SLO{}) ) +// NewSLO returns an SLO from metadata and spec. func NewSLO(metadata Metadata, spec SLOSpec) SLO { return SLO{ APIVersion: APIVersion, @@ -25,6 +26,8 @@ func NewSLO(metadata Metadata, spec SLOSpec) SLO { } } +// SLO represents a target value or range for a service level measured by an +// [SLI]. type SLO struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -32,49 +35,74 @@ type SLO struct { Spec SLOSpec `json:"spec"` } +// GetVersion returns [APIVersion]. func (s SLO) GetVersion() openslo.Version { return APIVersion } +// GetKind returns [openslo.KindSLO]. func (s SLO) GetKind() openslo.Kind { return openslo.KindSLO } +// GetName returns the name in the SLO's [Metadata]. func (s SLO) GetName() string { return s.Metadata.Name } +// Validate returns an error for an invalid SLO. func (s SLO) Validate() error { return sloValidation.Validate(s) } +// String returns the SLO's formatted version, kind, and name. func (s SLO) String() string { return internal.GetObjectName(s) } +// IsComposite reports whether the SLO has objective-level indicators. func (s SLO) IsComposite() bool { return s.Spec.HasCompositeObjectives() } +// GetMetadata returns the SLO's [Metadata]. func (s SLO) GetMetadata() Metadata { return s.Metadata } +// GetValidator returns the validator for SLO objects. func (s SLO) GetValidator() govy.Validator[SLO] { return sloValidation } +// SLOSpec defines the service association, indicator placement, budgeting +// method, evaluation window, objectives, and alert policies of an [SLO]. type SLOSpec struct { - Description string `json:"description,omitempty"` - Service string `json:"service"` - Indicator *SLOIndicatorInline `json:"indicator,omitempty"` - IndicatorRef *string `json:"indicatorRef,omitempty"` - BudgetingMethod SLOBudgetingMethod `json:"budgetingMethod"` - TimeWindow []SLOTimeWindow `json:"timeWindow,omitempty"` - Objectives []SLOObjective `json:"objectives"` - AlertPolicies []SLOAlertPolicy `json:"alertPolicies,omitempty"` + // Description summarizes the SLO. + Description string `json:"description,omitempty"` + // Service names the associated service. Consumers define how to resolve the + // name to a [Service]. + Service string `json:"service"` + // Indicator defines a standard SLO's SLI inline. Composite SLOs place + // indicators on individual Objectives. + Indicator *SLOIndicatorInline `json:"indicator,omitempty"` + // IndicatorRef names an existing [SLI] for a standard SLO. Composite SLOs + // place indicator references on individual Objectives. + IndicatorRef *string `json:"indicatorRef,omitempty"` + // BudgetingMethod applies the selected error-budget calculation to every + // objective. + BudgetingMethod SLOBudgetingMethod `json:"budgetingMethod"` + // TimeWindow contains the SLO's evaluation window. + TimeWindow []SLOTimeWindow `json:"timeWindow,omitempty"` + // Objectives contains the SLO's target definitions. + Objectives []SLOObjective `json:"objectives"` + // AlertPolicies contains inline alert policies or references to existing + // [AlertPolicy] objects. + AlertPolicies []SLOAlertPolicy `json:"alertPolicies,omitempty"` } +// HasCompositeObjectives reports whether any objective has an indicator. +// It does not verify that every objective in a composite SLO has one. func (s SLOSpec) HasCompositeObjectives() bool { for i := range s.Objectives { if s.Objectives[i].Indicator != nil || s.Objectives[i].IndicatorRef != nil { @@ -84,11 +112,25 @@ func (s SLOSpec) HasCompositeObjectives() bool { return false } +// SLOBudgetingMethod identifies how an [SLO] calculates objective success and +// error-budget use. +// Occurrences counts good events. Timeslices counts slices that meet +// [SLOObjective.TimeSliceTarget]. RatioTimeslices averages success ratios across +// slices. Composite SLOs apply objective weights to each calculation. type SLOBudgetingMethod string const ( - SLOBudgetingMethodOccurrences SLOBudgetingMethod = "Occurrences" - SLOBudgetingMethodTimeslices SLOBudgetingMethod = "Timeslices" + // SLOBudgetingMethodOccurrences uses the ratio of good events to total events, + // so traffic volume determines each period's influence. + // For a composite SLO, each objective's weight scales its burn rate. + SLOBudgetingMethodOccurrences SLOBudgetingMethod = "Occurrences" + // SLOBudgetingMethodTimeslices uses the ratio of slices meeting + // [SLOObjective.TimeSliceTarget] to all slices, giving each slice equal + // influence. Any bad objective makes a composite slice bad. + SLOBudgetingMethodTimeslices SLOBudgetingMethod = "Timeslices" + // SLOBudgetingMethodRatioTimeslices averages success ratios across slices + // without classifying them against [SLOObjective.TimeSliceTarget]. + // For a composite SLO, this method combines weighted deficits from 100 percent. SLOBudgetingMethodRatioTimeslices SLOBudgetingMethod = "RatioTimeslices" ) @@ -98,47 +140,78 @@ var validSLOBudgetingMethods = []SLOBudgetingMethod{ SLOBudgetingMethodRatioTimeslices, } +// SLOIndicatorInline embeds an [SLI] in an [SLOSpec] or [SLOObjective]. type SLOIndicatorInline struct { Metadata Metadata `json:"metadata"` Spec SLISpec `json:"spec"` } +// SLOObjective defines a success target and, when applicable, a threshold +// comparison or composite-specific indicator. For example, Target 0.995 and +// TargetPercent 99.5 both express a 99.5 percent target. type SLOObjective struct { - DisplayName string `json:"displayName,omitempty"` - Operator Operator `json:"op,omitempty"` - Value *float64 `json:"value,omitempty"` - Target *float64 `json:"target,omitempty"` - TargetPercent *float64 `json:"targetPercent,omitempty"` - TimeSliceTarget *float64 `json:"timeSliceTarget,omitempty"` - TimeSliceWindow *DurationShorthand `json:"timeSliceWindow,omitempty"` - Indicator *SLOIndicatorInline `json:"indicator,omitempty"` - IndicatorRef *string `json:"indicatorRef,omitempty"` - CompositeWeight *float64 `json:"compositeWeight,omitempty"` + // DisplayName is the objective's human-readable name. + DisplayName string `json:"displayName,omitempty"` + // Operator compares threshold-metric samples with Value. + Operator Operator `json:"op,omitempty"` + // Value sets the threshold for metric sample comparisons. + // It is distinct from the success target expressed by Target or TargetPercent. + Value *float64 `json:"value,omitempty"` + // Target expresses the success target as a fraction. + Target *float64 `json:"target,omitempty"` + // TargetPercent expresses the success target as a percentage. + TargetPercent *float64 `json:"targetPercent,omitempty"` + // TimeSliceTarget classifies a slice as good when BudgetingMethod is + // [SLOBudgetingMethodTimeslices]. + TimeSliceTarget *float64 `json:"timeSliceTarget,omitempty"` + // TimeSliceWindow sets the slice size and query-evaluation interval for + // [SLOBudgetingMethodTimeslices] and [SLOBudgetingMethodRatioTimeslices]. + TimeSliceWindow *DurationShorthand `json:"timeSliceWindow,omitempty"` + // Indicator defines this objective's SLI inline for a composite SLO. + Indicator *SLOIndicatorInline `json:"indicator,omitempty"` + // IndicatorRef names this objective's [SLI] for a composite SLO. + IndicatorRef *string `json:"indicatorRef,omitempty"` + // CompositeWeight scales this objective's contribution to a multi-objective + // composite SLO. OpenSLO uses a weight of 1 when omitted. This SDK leaves the + // field unset. + CompositeWeight *float64 `json:"compositeWeight,omitempty"` } +// SLOTimeWindow defines either a rolling or calendar-aligned evaluation window. type SLOTimeWindow struct { - Duration DurationShorthand `json:"duration"` - IsRolling bool `json:"isRolling"` - Calendar *SLOCalendar `json:"calendar,omitempty"` + // Duration is the length of the evaluation window. + Duration DurationShorthand `json:"duration"` + // IsRolling selects a rolling window when true and a calendar-aligned window + // when false. + IsRolling bool `json:"isRolling"` + // Calendar defines the alignment of a calendar window. + Calendar *SLOCalendar `json:"calendar,omitempty"` } +// SLOCalendar anchors a calendar-aligned [SLOTimeWindow] in a time zone. type SLOCalendar struct { + // StartTime anchors the first calendar window. StartTime string `json:"startTime"` - TimeZone string `json:"timeZone"` + // TimeZone controls the interpretation of StartTime and later boundaries. + TimeZone string `json:"timeZone"` } +// SLOAlertPolicy associates an alert policy with an [SLO]. type SLOAlertPolicy struct { *SLOAlertPolicyInline *SLOAlertPolicyRef } +// SLOAlertPolicyInline is an [AlertPolicy] embedded in an [SLO]. type SLOAlertPolicyInline struct { Kind openslo.Kind `json:"kind"` Metadata Metadata `json:"metadata"` Spec AlertPolicySpec `json:"spec"` } +// SLOAlertPolicyRef refers to an existing [AlertPolicy]. type SLOAlertPolicyRef struct { + // AlertPolicyRef names an existing alert policy. AlertPolicyRef string `json:"alertPolicyRef"` } @@ -196,7 +269,10 @@ var sloSpecValidation = govy.New( ), govy.ForSlice(func(spec SLOSpec) []SLOObjective { return spec.Objectives }). WithName("objectives"). - When(func(s SLOSpec) bool { return s.HasCompositeObjectives() }). + When( + func(s SLOSpec) bool { return s.HasCompositeObjectives() }, + govy.WhenDescription("is composite SLO"), + ). IncludeForEach(sloCompositeObjectiveValidation), ) @@ -209,7 +285,7 @@ func getSLOIndicatorValidation[T any]( Rules(rules.MutuallyExclusive(true, map[string]func(t T) any{ "indicator": func(t T) any { return indicatorGetter(t) }, "indicatorRef": func(t T) any { return indicatorRefGetter(t) }, - })), + }).WithDescription("exactly one of 'indicator' and 'indicatorRef' must be set")), govy.ForPointer(indicatorGetter). WithName("indicator"). Cascade(govy.CascadeModeContinue). @@ -225,7 +301,10 @@ func getSLOIndicatorValidation[T any]( ). // Another validation rule on 'spec' level already checks a scenario // in which neither 'indicator' nor 'indicatorRef' are provided. - When(func(t T) bool { return indicatorGetter(t) != nil || indicatorRefGetter(t) != nil }). + When( + func(t T) bool { return indicatorGetter(t) != nil || indicatorRefGetter(t) != nil }, + govy.WhenDescription("'indicator' or 'indicatorRef' is set"), + ). Cascade(govy.CascadeModeStop) } @@ -259,11 +338,11 @@ var sloTimeWindowValidation = govy.New( var sloAlertPolicyValidation = govy.New( govy.For(govy.GetSelf[SLOAlertPolicy]()). Rules(rules.MutuallyExclusive(true, map[string]func(a SLOAlertPolicy) any{ - "targetRef": func(a SLOAlertPolicy) any { return a.SLOAlertPolicyRef }, + "alertPolicyRef": func(a SLOAlertPolicy) any { return a.SLOAlertPolicyRef }, // It's impossible to list all fields that constitute the inlined version in the error message, // therefore 'spec' must suffice. "spec": func(a SLOAlertPolicy) any { return a.SLOAlertPolicyInline }, - })), + }).WithDescription("exactly one of 'alertPolicyRef' and 'spec' must be set")), govy.ForPointer(func(a SLOAlertPolicy) *SLOAlertPolicyRef { return a.SLOAlertPolicyRef }). @@ -297,7 +376,7 @@ var sloObjectiveValidation = govy.New( Rules(rules.MutuallyExclusive(true, map[string]func(o SLOObjective) any{ "target": func(o SLOObjective) any { return o.Target }, "targetPercent": func(o SLOObjective) any { return o.TargetPercent }, - })), + }).WithDescription("exactly one of 'target' and 'targetPercent' must be set")), govy.ForPointer(func(s SLOObjective) *float64 { return s.Target }). WithName("target"). Rules(rules.GTE(0.0), rules.LT(1.0)), @@ -351,7 +430,10 @@ var sloTimeSlicesObjectiveValidation = govy.New( validationRulesForTimeSliceWindow(), )), ). - When(func(s SLOSpec) bool { return s.BudgetingMethod == SLOBudgetingMethodTimeslices }) + When( + func(s SLOSpec) bool { return s.BudgetingMethod == SLOBudgetingMethodTimeslices }, + govy.WhenDescription("'budgetingMethod' is 'Timeslices'"), + ) var sloRatioTimeSlicesObjectiveValidation = govy.New( govy.ForSlice(func(spec SLOSpec) []SLOObjective { return spec.Objectives }). @@ -360,7 +442,10 @@ var sloRatioTimeSlicesObjectiveValidation = govy.New( validationRulesForTimeSliceWindow(), )), ). - When(func(s SLOSpec) bool { return s.BudgetingMethod == SLOBudgetingMethodRatioTimeslices }) + When( + func(s SLOSpec) bool { return s.BudgetingMethod == SLOBudgetingMethodRatioTimeslices }, + govy.WhenDescription("'budgetingMethod' is 'RatioTimeslices'"), + ) func validationRulesForTimeSliceWindow() govy.PropertyRules[DurationShorthand, SLOObjective] { return govy.ForPointer(func(s SLOObjective) *DurationShorthand { return s.TimeSliceWindow }). diff --git a/pkg/openslo/v1/slo_test.go b/pkg/openslo/v1/slo_test.go index ea2e29b..e911c18 100644 --- a/pkg/openslo/v1/slo_test.go +++ b/pkg/openslo/v1/slo_test.go @@ -446,6 +446,17 @@ func TestSLO_Validate_Spec_AlertPolicies(t *testing.T) { err := slo.Validate() govytest.AssertError(t, err, govytest.ExpectedRuleError{ PropertyPath: "spec.alertPolicies[0]", + Message: "[alertPolicyRef, spec] properties are mutually exclusive, provide only one of them", + Code: rules.ErrorCodeMutuallyExclusive, + }) + }) + t.Run("neither ref nor inline is set", func(t *testing.T) { + slo := validRatioSLO() + slo.Spec.AlertPolicies[0] = SLOAlertPolicy{} + err := slo.Validate() + govytest.AssertError(t, err, govytest.ExpectedRuleError{ + PropertyPath: "spec.alertPolicies[0]", + Message: "one of [alertPolicyRef, spec] properties must be set, none was provided", Code: rules.ErrorCodeMutuallyExclusive, }) }) diff --git a/pkg/openslo/v1alpha/doc.go b/pkg/openslo/v1alpha/doc.go index bea8e7b..ab64bdf 100644 --- a/pkg/openslo/v1alpha/doc.go +++ b/pkg/openslo/v1alpha/doc.go @@ -1,2 +1,2 @@ -// Package v1alpha defines the OpenSLO specification version v1alpha definitions. +// Package v1alpha defines OpenSLO v1alpha objects and supporting types. package v1alpha diff --git a/pkg/openslo/v1alpha/examples/slos.yaml b/pkg/openslo/v1alpha/examples/slos.yaml index dc56916..3126c49 100644 --- a/pkg/openslo/v1alpha/examples/slos.yaml +++ b/pkg/openslo/v1alpha/examples/slos.yaml @@ -44,7 +44,6 @@ - displayName: Good target: 0.995 timeSliceTarget: 0.95 - value: 1 ratioMetrics: incremental: true good: diff --git a/pkg/openslo/v1alpha/objects.go b/pkg/openslo/v1alpha/objects.go index 771619c..3e2ffe7 100644 --- a/pkg/openslo/v1alpha/objects.go +++ b/pkg/openslo/v1alpha/objects.go @@ -9,6 +9,7 @@ import ( "github.com/OpenSLO/go-sdk/pkg/openslo" ) +// APIVersion is the OpenSLO v1alpha API version. const APIVersion = openslo.VersionV1alpha var supportedKinds = []openslo.Kind{ @@ -16,17 +17,23 @@ var supportedKinds = []openslo.Kind{ openslo.KindService, } +// GetSupportedKinds returns the object kinds implemented by v1alpha. func GetSupportedKinds() []openslo.Kind { return slices.Clone(supportedKinds) } +// Object is an OpenSLO v1alpha object with version-specific [Metadata]. type Object interface { openslo.Object + // GetMetadata returns the object's metadata. GetMetadata() Metadata } +// Metadata identifies an OpenSLO v1alpha object. type Metadata struct { - Name string `json:"name"` + // Name is the object identifier used by references. + Name string `json:"name"` + // DisplayName is a human-readable name. DisplayName string `json:"displayName,omitempty"` } diff --git a/pkg/openslo/v1alpha/service.go b/pkg/openslo/v1alpha/service.go index 657db6b..d8d5164 100644 --- a/pkg/openslo/v1alpha/service.go +++ b/pkg/openslo/v1alpha/service.go @@ -13,6 +13,7 @@ var ( _ = openslo.ObjectValidator[Service](Service{}) ) +// NewService returns a service from metadata and spec. func NewService(metadata Metadata, spec ServiceSpec) Service { return Service{ APIVersion: APIVersion, @@ -22,6 +23,7 @@ func NewService(metadata Metadata, spec ServiceSpec) Service { } } +// Service groups related SLOs. type Service struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -29,35 +31,44 @@ type Service struct { Spec ServiceSpec `json:"spec"` } +// GetVersion returns [APIVersion]. func (s Service) GetVersion() openslo.Version { return APIVersion } +// GetKind returns [openslo.KindService]. func (s Service) GetKind() openslo.Kind { return openslo.KindService } +// GetName returns the service's metadata name. func (s Service) GetName() string { return s.Metadata.Name } +// Validate returns an error for an invalid service. func (s Service) Validate() error { return serviceValidation.Validate(s) } +// String returns the qualified object name. func (s Service) String() string { return internal.GetObjectName(s) } +// GetMetadata returns the service's metadata. func (s Service) GetMetadata() Metadata { return s.Metadata } +// GetValidator returns the validator used by [Service.Validate]. func (s Service) GetValidator() govy.Validator[Service] { return serviceValidation } +// ServiceSpec contains the descriptive properties of a [Service]. type ServiceSpec struct { + // Description summarizes the service. Description string `json:"description,omitempty"` } diff --git a/pkg/openslo/v1alpha/slo.go b/pkg/openslo/v1alpha/slo.go index f255741..64cc822 100644 --- a/pkg/openslo/v1alpha/slo.go +++ b/pkg/openslo/v1alpha/slo.go @@ -16,6 +16,7 @@ var ( _ = openslo.ObjectValidator[SLO](SLO{}) ) +// NewSLO returns an SLO from metadata and spec. func NewSLO(metadata Metadata, spec SLOSpec) SLO { return SLO{ APIVersion: APIVersion, @@ -25,6 +26,7 @@ func NewSLO(metadata Metadata, spec SLOSpec) SLO { } } +// SLO defines reliability targets for a service level measured by an indicator. type SLO struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -32,43 +34,62 @@ type SLO struct { Spec SLOSpec `json:"spec"` } +// GetVersion returns [APIVersion]. func (s SLO) GetVersion() openslo.Version { return APIVersion } +// GetKind returns [openslo.KindSLO]. func (s SLO) GetKind() openslo.Kind { return openslo.KindSLO } +// GetName returns the SLO's metadata name. func (s SLO) GetName() string { return s.Metadata.Name } +// Validate returns an error for an invalid SLO. func (s SLO) Validate() error { return sloValidation.Validate(s) } +// String returns the qualified object name. func (s SLO) String() string { return internal.GetObjectName(s) } +// GetMetadata returns the SLO's metadata. func (s SLO) GetMetadata() Metadata { return s.Metadata } +// GetValidator returns the validator used by [SLO.Validate]. func (s SLO) GetValidator() govy.Validator[SLO] { return sloValidation } +// SLOSpec defines the service, indicator, objectives, time window, and +// error-budget calculation for an [SLO]. type SLOSpec struct { - TimeWindows []SLOTimeWindow `json:"timeWindows"` + // TimeWindows contains the SLO evaluation window. + TimeWindows []SLOTimeWindow `json:"timeWindows"` + // BudgetingMethod applies the selected error-budget calculation to every + // objective. BudgetingMethod SLOBudgetingMethod `json:"budgetingMethod"` - Description string `json:"description,omitempty"` - Indicator *SLOIndicator `json:"indicator"` - Service string `json:"service"` - Objectives []SLOObjective `json:"objectives"` + // Description summarizes the SLO. + Description string `json:"description,omitempty"` + // Indicator defines the threshold-metric form of the SLO. + Indicator *SLOIndicator `json:"indicator"` + // Service identifies the service whose reliability the SLO measures. + Service string `json:"service"` + // Objectives contains the targets used to evaluate the indicator. + Objectives []SLOObjective `json:"objectives"` } +// SLOBudgetingMethod identifies how an SLO calculates its error budget. +// Occurrences weights each event equally. Timeslices weights each time slice +// equally. type SLOBudgetingMethod string const ( @@ -81,38 +102,69 @@ var validSLOBudgetingMethods = []SLOBudgetingMethod{ SLOBudgetingMethodTimeslices, } +// SLOIndicator defines the threshold-metric form of a v1alpha service level +// indicator. type SLOIndicator struct { + // ThresholdMetric retrieves raw metric values. Each objective compares them + // with its [Operator] and Value. ThresholdMetric SLOMetricSourceSpec `json:"thresholdMetric"` } +// SLOMetricSourceSpec describes a provider-specific metric query. type SLOMetricSourceSpec struct { - Source string `json:"source"` + // Source identifies the metric data source. + Source string `json:"source"` + // QueryType identifies the query language or query form. QueryType string `json:"queryType"` - Query string `json:"query"` + // Query is the provider-specific expression that retrieves the metric. + Query string `json:"query"` } +// SLOObjective defines a reliability target and, for the ratio form, its metric queries. type SLOObjective struct { - DisplayName string `json:"displayName"` - Value *float64 `json:"value"` - RatioMetrics *SLORatioMetrics `json:"ratioMetrics"` - BudgetTarget *float64 `json:"target"` - TimeSliceTarget *float64 `json:"timeSliceTarget,omitempty"` - Operator Operator `json:"op,omitempty"` + // DisplayName is a human-readable objective name. + DisplayName string `json:"displayName"` + // Value is the metric threshold used by [Operator]. + Value *float64 `json:"value,omitempty"` + // RatioMetrics supplies a good-events-to-total-events indicator. + RatioMetrics *SLORatioMetrics `json:"ratioMetrics"` + // BudgetTarget is the desired fraction of good events or time slices. + BudgetTarget *float64 `json:"target"` + // TimeSliceTarget is the minimum success ratio that makes a time slice good. + // It is used by the Timeslices budgeting method. + TimeSliceTarget *float64 `json:"timeSliceTarget,omitempty"` + // Operator compares values returned by the threshold metric with Value. + Operator Operator `json:"op,omitempty"` } +// SLORatioMetrics defines an indicator as the ratio of good events to total +// events. For example, 99 successful requests out of 100 total requests produce +// a ratio of 0.99. type SLORatioMetrics struct { - Good SLOMetricSourceSpec `json:"good"` - Total SLOMetricSourceSpec `json:"total"` - Incremental bool `json:"incremental"` + // Good retrieves the numerator: events considered successful. + Good SLOMetricSourceSpec `json:"good"` + // Total retrieves the denominator: all considered events. + Total SLOMetricSourceSpec `json:"total"` + // Incremental reports whether the queried metrics are monotonically + // increasing counters rather than values that can rise or fall. + Incremental bool `json:"incremental"` } +// SLOTimeWindow defines the period over which an SLO is evaluated. For example, +// a Unit of Week and a Count of 4 define a four-week window. type SLOTimeWindow struct { - Unit SLOTimeWindowUnit `json:"unit"` - Count int `json:"count"` - IsRolling bool `json:"isRolling"` - Calendar *SLOCalendar `json:"calendar,omitempty"` + // Unit combines with Count to set the window length. + Unit SLOTimeWindowUnit `json:"unit"` + // Count sets how many Units form the window. + Count int `json:"count"` + // IsRolling selects a continuously advancing window when true and a + // calendar-aligned window when false. + IsRolling bool `json:"isRolling"` + // Calendar defines the alignment of a calendar window. + Calendar *SLOCalendar `json:"calendar,omitempty"` } +// SLOTimeWindowUnit identifies the unit used to express an [SLOTimeWindow]. type SLOTimeWindowUnit string const ( @@ -131,11 +183,16 @@ var validSLOTimeWindowUnits = []SLOTimeWindowUnit{ SLOTimeWindowUnitQuarter, } +// SLOCalendar anchors a calendar-aligned [SLOTimeWindow]. type SLOCalendar struct { + // StartTime is the local anchor in YYYY-MM-DD HH:MM:SS format, for example + // 2026-07-25 14:30:00. StartTime string `json:"startTime"` - TimeZone string `json:"timeZone"` + // TimeZone is the IANA time-zone name used to interpret StartTime. + TimeZone string `json:"timeZone"` } +// Operator selects the comparison between a threshold metric and an objective value. type Operator string const ( @@ -177,7 +234,9 @@ var sloValidation = govy.New( return errors.New("one of 'indicator' or 'objectives[*].ratioMetrics' must be set") } return nil - }).WithErrorCode(rules.ErrorCodeMutuallyExclusive), + }). + WithDescription("exactly one of 'indicator' and 'objectives[*].ratioMetrics' must be set"). + WithErrorCode(rules.ErrorCodeMutuallyExclusive), ). Include(sloSpecValidation), ).WithNameFunc(internal.GetObjectName[SLO]) @@ -199,7 +258,7 @@ var sloSpecValidation = govy.New( Required(). Rules(rules.OneOf(validSLOBudgetingMethods...)), govy.ForSlice(func(spec SLOSpec) []SLOTimeWindow { return spec.TimeWindows }). - WithName("timeWindow"). + WithName("timeWindows"). Rules(rules.SliceLength[[]SLOTimeWindow](1, 1)). IncludeForEach(sloTimeWindowValidation), govy.ForSlice(func(spec SLOSpec) []SLOObjective { return spec.Objectives }). @@ -253,6 +312,10 @@ var sloObjectiveValidation = govy.New( Include(sloRatioMetricsValidation), govy.ForPointer(func(s SLOObjective) *float64 { return s.Value }). WithName("value"). + When( + func(s SLOObjective) bool { return s.RatioMetrics == nil }, + govy.WhenDescription("'ratioMetrics' is not set"), + ). Required(), govy.ForPointer(func(s SLOObjective) *float64 { return s.BudgetTarget }). WithName("target"). @@ -262,7 +325,7 @@ var sloObjectiveValidation = govy.New( WithName("op"). When( func(s SLOObjective) bool { return s.RatioMetrics == nil }, - govy.WhenDescription("'thresholdMetric' is set"), + govy.WhenDescription("'ratioMetrics' is not set"), ). Required(). Rules(rules.OneOf(validOperators...)), @@ -285,7 +348,10 @@ var sloTimeSlicesObjectiveValidation = govy.New( Rules(rules.GTE(0.0), rules.LTE(1.0)), )), ). - When(func(s SLOSpec) bool { return s.BudgetingMethod == SLOBudgetingMethodTimeslices }) + When( + func(s SLOSpec) bool { return s.BudgetingMethod == SLOBudgetingMethodTimeslices }, + govy.WhenDescription("'budgetingMethod' is 'Timeslices'"), + ) var sloRatioMetricsValidation = govy.New( govy.For(func(s SLORatioMetrics) SLOMetricSourceSpec { return s.Good }). diff --git a/pkg/openslo/v1alpha/slo_example_test.go b/pkg/openslo/v1alpha/slo_example_test.go index 8054b4f..646ae5a 100644 --- a/pkg/openslo/v1alpha/slo_example_test.go +++ b/pkg/openslo/v1alpha/slo_example_test.go @@ -32,7 +32,6 @@ func ExampleSLO() { - displayName: Good target: 0.995 timeSliceTarget: 0.95 - value: 1 ratioMetrics: incremental: true good: @@ -70,7 +69,6 @@ func ExampleSLO() { DisplayName: "Good", BudgetTarget: new(0.995), TimeSliceTarget: new(0.95), - Value: new(1.0), RatioMetrics: &v1alpha.SLORatioMetrics{ Incremental: true, Good: v1alpha.SLOMetricSourceSpec{ @@ -130,7 +128,6 @@ func ExampleSLO() { // source: datadog // target: 0.995 // timeSliceTarget: 0.95 - // value: 1 // service: web // timeWindows: // - calendar: diff --git a/pkg/openslo/v1alpha/slo_test.go b/pkg/openslo/v1alpha/slo_test.go index 7915896..9c064a9 100644 --- a/pkg/openslo/v1alpha/slo_test.go +++ b/pkg/openslo/v1alpha/slo_test.go @@ -2,6 +2,7 @@ package v1alpha import ( "fmt" + "slices" "strings" "testing" @@ -24,6 +25,67 @@ func TestSLO_Validate_Ok(t *testing.T) { } } +func TestSLO_ValidationPlan(t *testing.T) { + plan, err := govy.Plan(sloValidation, govy.PlanStrictMode()) + assert.Require(t, assert.NoError(t, err)) + + assertValidationPlanRule( + t, + plan, + "$.spec", + "exactly one of 'indicator' and 'objectives[*].ratioMetrics' must be set", + ) + assertValidationPlanRule( + t, + plan, + "$.spec.objectives[*].value", + "property is required", + "'ratioMetrics' is not set", + ) + assertValidationPlanRule( + t, + plan, + "$.spec.objectives[*].op", + "property is required", + "'ratioMetrics' is not set", + ) + assertValidationPlanRule( + t, + plan, + "$.spec.objectives[*].op", + "must be one of: gt, lt, gte, lte", + "'ratioMetrics' is not set", + ) +} + +func assertValidationPlanRule( + t *testing.T, + plan *govy.ValidatorPlan, + path string, + description string, + conditions ...string, +) { + t.Helper() + for _, property := range plan.Properties { + if property.Path.String() != path { + continue + } + for _, rule := range property.Rules { + if rule.Description != description { + continue + } + for _, condition := range conditions { + if !slices.Contains(rule.Conditions, condition) { + t.Errorf("validation rule %q at %s does not have condition %q", description, path, condition) + return + } + } + return + } + } + t.Errorf("validation plan does not contain rule %q at %s", description, path) +} + func TestSLO_Validate_VersionAndKind(t *testing.T) { slo := validSLO() slo.APIVersion = "v0.1" @@ -144,12 +206,12 @@ func TestSLO_Validate_Spec(t *testing.T) { } func TestSLO_Validate_Spec_TimeWindows(t *testing.T) { - t.Run("missing timeWindow", func(t *testing.T) { + t.Run("missing timeWindows", func(t *testing.T) { slo := validSLO() slo.Spec.TimeWindows = []SLOTimeWindow{} err := slo.Validate() govytest.AssertError(t, err, govytest.ExpectedRuleError{ - PropertyPath: "spec.timeWindow", + PropertyPath: "spec.timeWindows", Code: rules.ErrorCodeSliceLength, }) }) @@ -161,7 +223,7 @@ func TestSLO_Validate_Spec_TimeWindows(t *testing.T) { } err := slo.Validate() govytest.AssertError(t, err, govytest.ExpectedRuleError{ - PropertyPath: "spec.timeWindow", + PropertyPath: "spec.timeWindows", Code: rules.ErrorCodeSliceLength, }) }) @@ -200,10 +262,16 @@ func TestSLO_Validate_Spec_Objectives(t *testing.T) { Code: rules.ErrorCodeRequired, }) }) - t.Run("value is missing", func(t *testing.T) { + t.Run("ratioMetrics - value missing", func(t *testing.T) { slo := validSLO() slo.Spec.Objectives[0].Value = nil err := slo.Validate() + govytest.AssertNoError(t, err) + }) + t.Run("threshold - value missing", func(t *testing.T) { + slo := validThresholdSLO() + slo.Spec.Objectives[0].Value = nil + err := slo.Validate() govytest.AssertError(t, err, govytest.ExpectedRuleError{ PropertyPath: "spec.objectives[0].value", Code: rules.ErrorCodeRequired, diff --git a/pkg/openslo/v2alpha/alert_condition.go b/pkg/openslo/v2alpha/alert_condition.go index 6b6cf4e..c4d6c05 100644 --- a/pkg/openslo/v2alpha/alert_condition.go +++ b/pkg/openslo/v2alpha/alert_condition.go @@ -13,6 +13,7 @@ var ( _ = openslo.ObjectValidator[AlertCondition](AlertCondition{}) ) +// NewAlertCondition returns an AlertCondition from metadata and spec. func NewAlertCondition(metadata Metadata, spec AlertConditionSpec) AlertCondition { return AlertCondition{ APIVersion: APIVersion, @@ -22,6 +23,7 @@ func NewAlertCondition(metadata Metadata, spec AlertConditionSpec) AlertConditio } } +// AlertCondition defines when an SLO alert is considered breaching. type AlertCondition struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -29,48 +31,71 @@ type AlertCondition struct { Spec AlertConditionSpec `json:"spec"` } +// GetVersion returns [APIVersion]. func (a AlertCondition) GetVersion() openslo.Version { return APIVersion } +// GetKind returns [openslo.KindAlertCondition]. func (a AlertCondition) GetKind() openslo.Kind { return openslo.KindAlertCondition } +// GetName returns the alert condition's metadata name. func (a AlertCondition) GetName() string { return a.Metadata.Name } +// Validate returns an error for an invalid alert condition. func (a AlertCondition) Validate() error { return alertConditionValidation.Validate(a) } +// String returns the alert condition's formatted version, kind, and name. func (a AlertCondition) String() string { return internal.GetObjectName(a) } +// GetMetadata returns the alert condition's metadata. func (a AlertCondition) GetMetadata() Metadata { return a.Metadata } +// GetValidator returns the validator configured for [AlertCondition]. func (a AlertCondition) GetValidator() govy.Validator[AlertCondition] { return alertConditionValidation } +// AlertConditionSpec defines an alert's severity and breach condition. type AlertConditionSpec struct { - Severity string `json:"severity"` - Condition AlertConditionType `json:"condition"` - Description string `json:"description,omitempty"` + // Severity is a consumer-defined alert classification. + Severity string `json:"severity"` + Condition AlertConditionType `json:"condition"` + // Description summarizes the alert condition. + Description string `json:"description,omitempty"` } +// AlertConditionType defines a burn-rate comparison over a lookback window. +// Burn rate is error-budget consumption relative to the rate allowed by the +// SLO. type AlertConditionType struct { - Kind AlertConditionKind `json:"kind"` - Operator Operator `json:"op"` - Threshold *float64 `json:"threshold"` - LookbackWindow DurationShorthand `json:"lookbackWindow"` - AlertAfter DurationShorthand `json:"alertAfter"` + // Kind selects the condition algorithm. OpenSLO defaults omitted values to + // [AlertConditionKindBurnRate]. This SDK does not apply that default. + Kind AlertConditionKind `json:"kind"` + // Operator compares the calculated burn rate with Threshold. + Operator Operator `json:"op"` + // Threshold sets the numeric burn-rate boundary. + Threshold *float64 `json:"threshold"` + // LookbackWindow sets the period for burn-rate calculation. + LookbackWindow DurationShorthand `json:"lookbackWindow"` + // AlertAfter sets how long the comparison must hold before the condition + // breaches. OpenSLO defaults omitted values to "0m". This SDK does not apply + // that default. + AlertAfter DurationShorthand `json:"alertAfter"` } +// AlertConditionKind identifies the evaluation algorithm for an +// [AlertConditionType]. type AlertConditionKind string const ( @@ -127,4 +152,7 @@ var alertConditionBurnRateValidation = govy.New( Required(). Include(durationShortHandValidation), ). - When(func(a AlertConditionType) bool { return a.Kind == AlertConditionKindBurnRate }) + When( + func(a AlertConditionType) bool { return a.Kind == AlertConditionKindBurnRate }, + govy.WhenDescription("'kind' is 'burnrate'"), + ) diff --git a/pkg/openslo/v2alpha/alert_condition_test.go b/pkg/openslo/v2alpha/alert_condition_test.go index 9dde2a0..381a63c 100644 --- a/pkg/openslo/v2alpha/alert_condition_test.go +++ b/pkg/openslo/v2alpha/alert_condition_test.go @@ -1,9 +1,11 @@ package v2alpha import ( + "slices" "strings" "testing" + "github.com/nobl9/govy/pkg/govy" "github.com/nobl9/govy/pkg/govytest" "github.com/nobl9/govy/pkg/rules" @@ -53,6 +55,19 @@ func TestAlertCondition_Validate_Spec(t *testing.T) { }) } +func TestAlertCondition_ValidationPlan(t *testing.T) { + plan, err := govy.Plan(alertConditionValidation, govy.PlanStrictMode()) + assert.Require(t, assert.NoError(t, err)) + + assertValidationPlanRule( + t, + plan, + "$.spec.condition.op", + "must be one of: gt, lt, gte, lte", + "'kind' is 'burnrate'", + ) +} + func runAlertConditionSpecTests[T openslo.Object]( t *testing.T, path string, @@ -210,3 +225,31 @@ func validAlertCondition() AlertCondition { }, ) } + +func assertValidationPlanRule( + t *testing.T, + plan *govy.ValidatorPlan, + path string, + description string, + conditions ...string, +) { + t.Helper() + for _, property := range plan.Properties { + if property.Path.String() != path { + continue + } + for _, rule := range property.Rules { + if rule.Description != description { + continue + } + for _, condition := range conditions { + if !slices.Contains(rule.Conditions, condition) { + t.Errorf("validation rule %q at %s does not have condition %q", description, path, condition) + return + } + } + return + } + } + t.Errorf("validation plan does not contain rule %q at %s", description, path) +} diff --git a/pkg/openslo/v2alpha/alert_notification_target.go b/pkg/openslo/v2alpha/alert_notification_target.go index 7a22b11..b6b357b 100644 --- a/pkg/openslo/v2alpha/alert_notification_target.go +++ b/pkg/openslo/v2alpha/alert_notification_target.go @@ -13,6 +13,7 @@ var ( _ = openslo.ObjectValidator[AlertNotificationTarget](AlertNotificationTarget{}) ) +// NewAlertNotificationTarget returns a notification target from metadata and spec. func NewAlertNotificationTarget(metadata Metadata, spec AlertNotificationTargetSpec) AlertNotificationTarget { return AlertNotificationTarget{ APIVersion: APIVersion, @@ -22,6 +23,7 @@ func NewAlertNotificationTarget(metadata Metadata, spec AlertNotificationTargetS } } +// AlertNotificationTarget names a destination for alert delivery. type AlertNotificationTarget struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -29,37 +31,48 @@ type AlertNotificationTarget struct { Spec AlertNotificationTargetSpec `json:"spec"` } +// GetVersion returns [APIVersion]. func (a AlertNotificationTarget) GetVersion() openslo.Version { return APIVersion } +// GetKind returns [openslo.KindAlertNotificationTarget]. func (a AlertNotificationTarget) GetKind() openslo.Kind { return openslo.KindAlertNotificationTarget } +// GetName returns the notification target's metadata name. func (a AlertNotificationTarget) GetName() string { return a.Metadata.Name } +// Validate returns an error for an invalid notification target. func (a AlertNotificationTarget) Validate() error { return alertNotificationTargetValidation.Validate(a) } +// String returns the notification target's formatted version, kind, and name. func (a AlertNotificationTarget) String() string { return internal.GetObjectName(a) } +// GetMetadata returns the notification target's metadata. func (a AlertNotificationTarget) GetMetadata() Metadata { return a.Metadata } +// GetValidator returns the validator configured for [AlertNotificationTarget]. func (a AlertNotificationTarget) GetValidator() govy.Validator[AlertNotificationTarget] { return alertNotificationTargetValidation } +// AlertNotificationTargetSpec identifies the consumer-defined notification +// destination. type AlertNotificationTargetSpec struct { + // Description summarizes the destination. Description string `json:"description,omitempty"` - Target string `json:"target"` + // Target identifies the consumer-defined destination for alert delivery. + Target string `json:"target"` } var alertNotificationTargetValidation = govy.New( diff --git a/pkg/openslo/v2alpha/alert_policy.go b/pkg/openslo/v2alpha/alert_policy.go index 14ca270..96329d7 100644 --- a/pkg/openslo/v2alpha/alert_policy.go +++ b/pkg/openslo/v2alpha/alert_policy.go @@ -13,6 +13,7 @@ var ( _ = openslo.ObjectValidator[AlertPolicy](AlertPolicy{}) ) +// NewAlertPolicy returns an AlertPolicy from metadata and spec. func NewAlertPolicy(metadata Metadata, spec AlertPolicySpec) AlertPolicy { return AlertPolicy{ APIVersion: APIVersion, @@ -22,6 +23,7 @@ func NewAlertPolicy(metadata Metadata, spec AlertPolicySpec) AlertPolicy { } } +// AlertPolicy defines when the system emits an SLO alert and where it sends it. type AlertPolicy struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -29,70 +31,99 @@ type AlertPolicy struct { Spec AlertPolicySpec `json:"spec"` } +// GetVersion returns [APIVersion]. func (a AlertPolicy) GetVersion() openslo.Version { return APIVersion } +// GetKind returns [openslo.KindAlertPolicy]. func (a AlertPolicy) GetKind() openslo.Kind { return openslo.KindAlertPolicy } +// GetName returns the alert policy's metadata name. func (a AlertPolicy) GetName() string { return a.Metadata.Name } +// Validate returns an error for an invalid alert policy. func (a AlertPolicy) Validate() error { return alertPolicyValidation.Validate(a) } +// String returns the alert policy's formatted version, kind, and name. func (a AlertPolicy) String() string { return internal.GetObjectName(a) } +// GetMetadata returns the alert policy's metadata. func (a AlertPolicy) GetMetadata() Metadata { return a.Metadata } +// GetValidator returns the validator configured for [AlertPolicy]. func (a AlertPolicy) GetValidator() govy.Validator[AlertPolicy] { return alertPolicyValidation } +// AlertPolicySpec defines the trigger states, condition, and notification +// destinations for an [AlertPolicy]. type AlertPolicySpec struct { - Description string `json:"description,omitempty"` - AlertWhenNoData bool `json:"alertWhenNoData,omitempty"` - AlertWhenBreaching bool `json:"alertWhenBreaching,omitempty"` - AlertWhenResolved bool `json:"alertWhenResolved,omitempty"` - Conditions []AlertPolicyCondition `json:"conditions,omitempty"` + // Description summarizes the alert policy. + Description string `json:"description,omitempty"` + // AlertWhenNoData enables notifications when the associated SLO has no + // burn-rate value. + AlertWhenNoData bool `json:"alertWhenNoData,omitempty"` + // AlertWhenBreaching enables notifications when the condition starts breaching. + AlertWhenBreaching bool `json:"alertWhenBreaching,omitempty"` + // AlertWhenResolved enables notifications when the condition resolves. + AlertWhenResolved bool `json:"alertWhenResolved,omitempty"` + // Conditions supplies the policy's alert condition by reference or inline. + Conditions []AlertPolicyCondition `json:"conditions,omitempty"` + // NotificationTargets lists referenced or inline delivery destinations. NotificationTargets []AlertPolicyNotificationTarget `json:"notificationTargets,omitempty"` } +// AlertPolicyCondition supplies a condition to an [AlertPolicy] by reference +// or inline definition. type AlertPolicyCondition struct { *AlertPolicyConditionRef *AlertPolicyConditionInline } +// AlertPolicyConditionInline is an alert-condition definition embedded in an +// [AlertPolicy]. type AlertPolicyConditionInline struct { Kind openslo.Kind `json:"kind"` Metadata Metadata `json:"metadata"` Spec AlertConditionSpec `json:"spec"` } +// AlertPolicyConditionRef identifies a separately defined [AlertCondition]. type AlertPolicyConditionRef struct { + // ConditionRef names the alert condition to use. ConditionRef string `json:"conditionRef"` } +// AlertPolicyNotificationTarget supplies a notification destination to an +// [AlertPolicy] by reference or inline definition. type AlertPolicyNotificationTarget struct { *AlertPolicyNotificationTargetRef *AlertPolicyNotificationTargetInline } +// AlertPolicyNotificationTargetInline is an alert-notification-target +// definition embedded in an [AlertPolicy]. type AlertPolicyNotificationTargetInline struct { Kind openslo.Kind `json:"kind"` Metadata Metadata `json:"metadata"` Spec AlertNotificationTargetSpec `json:"spec"` } +// AlertPolicyNotificationTargetRef identifies a separately defined +// [AlertNotificationTarget]. type AlertPolicyNotificationTargetRef struct { + // TargetRef names the notification target to use. TargetRef string `json:"targetRef"` } @@ -126,7 +157,8 @@ var alertPolicyConditionValidation = govy.New( // It's impossible to list all fields that constitute the inlined version in the error message, // therefore 'spec' must suffice. "spec": func(a AlertPolicyCondition) any { return a.AlertPolicyConditionInline }, - })), + }). + WithDescription("exactly one of 'conditionRef' and 'spec' must be set")), govy.ForPointer(func(a AlertPolicyCondition) *AlertPolicyConditionRef { return a.AlertPolicyConditionRef }). Include(govy.New( govy.For(func(ref AlertPolicyConditionRef) string { return ref.ConditionRef }). @@ -155,7 +187,8 @@ var alertPolicyNotificationTargetValidation = govy.New( // It's impossible to list all fields that constitute the inlined version in the error message, // therefore 'spec' must suffice. "spec": func(a AlertPolicyNotificationTarget) any { return a.AlertPolicyNotificationTargetInline }, - })), + }). + WithDescription("exactly one of 'targetRef' and 'spec' must be set")), govy.ForPointer(func(a AlertPolicyNotificationTarget) *AlertPolicyNotificationTargetRef { return a.AlertPolicyNotificationTargetRef }). diff --git a/pkg/openslo/v2alpha/alert_policy_test.go b/pkg/openslo/v2alpha/alert_policy_test.go index 48a1ef9..54ef1f1 100644 --- a/pkg/openslo/v2alpha/alert_policy_test.go +++ b/pkg/openslo/v2alpha/alert_policy_test.go @@ -5,6 +5,7 @@ import ( "strings" "testing" + "github.com/nobl9/govy/pkg/govy" "github.com/nobl9/govy/pkg/govytest" "github.com/nobl9/govy/pkg/rules" @@ -59,6 +60,27 @@ func TestAlertPolicy_Validate_Spec(t *testing.T) { }) } +func TestAlertPolicy_ValidationPlan(t *testing.T) { + plan, err := govy.Plan(alertPolicyValidation, govy.PlanStrictMode()) + assert.Require(t, assert.NoError(t, err)) + + for _, test := range []struct { + path string + description string + }{ + { + path: "$.spec.conditions[*]", + description: "exactly one of 'conditionRef' and 'spec' must be set", + }, + { + path: "$.spec.notificationTargets[*]", + description: "exactly one of 'targetRef' and 'spec' must be set", + }, + } { + assertValidationPlanRule(t, plan, test.path, test.description) + } +} + func runAlertPolicySpecTests[T openslo.Object]( t *testing.T, path string, diff --git a/pkg/openslo/v2alpha/data_source.go b/pkg/openslo/v2alpha/data_source.go index 78956b8..730524b 100644 --- a/pkg/openslo/v2alpha/data_source.go +++ b/pkg/openslo/v2alpha/data_source.go @@ -15,6 +15,7 @@ var ( _ = openslo.ObjectValidator[DataSource](DataSource{}) ) +// NewDataSource returns a data source from metadata and spec. func NewDataSource(metadata Metadata, spec DataSourceSpec) DataSource { return DataSource{ APIVersion: APIVersion, @@ -24,6 +25,7 @@ func NewDataSource(metadata Metadata, spec DataSourceSpec) DataSource { } } +// DataSource makes metric-source connection details reusable across SLIs. type DataSource struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -31,37 +33,49 @@ type DataSource struct { Spec DataSourceSpec `json:"spec"` } +// GetVersion returns [APIVersion]. func (d DataSource) GetVersion() openslo.Version { return APIVersion } +// GetKind returns [openslo.KindDataSource]. func (d DataSource) GetKind() openslo.Kind { return openslo.KindDataSource } +// GetName returns the data source's metadata name. func (d DataSource) GetName() string { return d.Metadata.Name } +// Validate returns an error for an invalid data source. func (d DataSource) Validate() error { return dataSourceValidation.Validate(d) } +// String returns the data source's formatted version, kind, and name. func (d DataSource) String() string { return internal.GetObjectName(d) } +// GetMetadata returns the data source's metadata. func (d DataSource) GetMetadata() Metadata { return d.Metadata } +// GetValidator returns the validator configured for [DataSource]. func (d DataSource) GetValidator() govy.Validator[DataSource] { return dataSourceValidation } +// DataSourceSpec defines a data-source type and its provider-specific connection +// configuration. type DataSourceSpec struct { - Description string `json:"description,omitempty"` - Type string `json:"type"` + // Description summarizes the data source. + Description string `json:"description,omitempty"` + // Type identifies the data-source implementation understood by the consumer. + Type string `json:"type"` + // ConnectionDetails stores provider-specific connection configuration as JSON. ConnectionDetails json.RawMessage `json:"connectionDetails"` } diff --git a/pkg/openslo/v2alpha/doc.go b/pkg/openslo/v2alpha/doc.go index 18eb189..ad21618 100644 --- a/pkg/openslo/v2alpha/doc.go +++ b/pkg/openslo/v2alpha/doc.go @@ -1,4 +1,18 @@ -// Package v2alpha contains the OpenSLO specification version v2alpha definitions. -// It is a prototype of the next version of the OpenSLO specification. -// It is not stable and is subject to breaking changes. +// Package v2alpha contains Go representations and validators for the unstable +// [OpenSLO v2alpha proposal]. This package can change incompatibly as the +// proposal changes. +// +// Objects use the "openslo.com/v2alpha" API version and Kubernetes-style +// [Metadata]. The metadata has one value per label and no display name. +// Compared with v1, SLO indicator fields use the names "sli" and "sliRef". +// Metric source fields are "dataSourceRef", "dataSourceSpec", and "spec". +// Durations support minutes, hours, days, and weeks. Threshold-metric SLOs can +// have multiple objectives. +// +// The proposal also describes labels on individual SLO objectives, but +// [SLOObjective] does not currently expose an objective-label field. +// Where the proposal is incomplete, the exported fields, JSON tags, and +// validators define the SDK representation. +// +// [OpenSLO v2alpha proposal]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md package v2alpha diff --git a/pkg/openslo/v2alpha/duration_shorthand.go b/pkg/openslo/v2alpha/duration_shorthand.go index 15ed750..06a2198 100644 --- a/pkg/openslo/v2alpha/duration_shorthand.go +++ b/pkg/openslo/v2alpha/duration_shorthand.go @@ -8,14 +8,15 @@ import ( "github.com/nobl9/govy/pkg/rules" ) -// ParseDurationShorthand parses a string representation of [DurationShorthand]. +// ParseDurationShorthand parses s into a [DurationShorthand]. func ParseDurationShorthand(s string) (DurationShorthand, error) { d := new(DurationShorthand) err := d.UnmarshalText([]byte(s)) return *d, err } -// NewDurationShorthand creates a new [DurationShorthand] instance. +// NewDurationShorthand returns a shorthand with the supplied value and unit +// without validating them. func NewDurationShorthand(value int, unit DurationShorthandUnit) DurationShorthand { return DurationShorthand{ unit: unit, @@ -23,32 +24,25 @@ func NewDurationShorthand(value int, unit DurationShorthandUnit) DurationShortha } } -// DurationShorthand is a shorthand representation of time duration. -// It consists of a value and unit, e.g. '1m' (1 minute), '10d' (10 days). +// DurationShorthand represents a duration as an integer with suffix "m", "h", +// "d", or "w". This SDK accepts zero and encodes it as empty text. OpenSLO +// specifies a positive integer. type DurationShorthand struct { unit DurationShorthandUnit value int } -// GetUnit returns the underlying [DurationShorthandUnit]. -// Example: -// -// duration, _ := ParseDurationShorthand("1w") -// duration.GetUnit() -> "w" +// GetUnit returns the shorthand's [DurationShorthandUnit]. func (d *DurationShorthand) GetUnit() DurationShorthandUnit { return d.unit } -// GetValue returns the underlying duration value. -// Example: -// -// duration, _ := ParseDurationShorthand("12w") -// duration.GetValue() -> "12" +// GetValue returns the shorthand's integer value. func (d *DurationShorthand) GetValue() int { return d.value } -// UnmarshalText implements [encoding.TextUnmarshaler]. +// UnmarshalText decodes text into d as an [encoding.TextUnmarshaler]. func (d *DurationShorthand) UnmarshalText(text []byte) error { if len(text) == 0 { return nil @@ -59,7 +53,7 @@ func (d *DurationShorthand) UnmarshalText(text []byte) error { return nil } -// MarshalText implements [encoding.TextMarshaler]. +// MarshalText encodes d as an [encoding.TextMarshaler]. func (d DurationShorthand) MarshalText() ([]byte, error) { if d.value == 0 { return []byte{}, nil @@ -67,7 +61,7 @@ func (d DurationShorthand) MarshalText() ([]byte, error) { return []byte(d.String()), nil } -// String implements [fmt.Stringer]. +// String returns the encoded shorthand as required by [fmt.Stringer]. func (d DurationShorthand) String() string { if d.value == 0 { return "" @@ -75,7 +69,7 @@ func (d DurationShorthand) String() string { return fmt.Sprintf("%d%s", d.value, d.unit) } -// Duration returns the [time.Duration] representation of [DurationShorthand]. +// Duration returns the equivalent [time.Duration] and panics for an unsupported unit. func (d DurationShorthand) Duration() time.Duration { switch d.unit { case DurationShorthandUnitMinute: @@ -91,7 +85,8 @@ func (d DurationShorthand) Duration() time.Duration { } } -// DurationShorthandUnit is a unit of [DurationShorthand]. +// DurationShorthandUnit identifies a minute, hour, day, or week unit for +// [DurationShorthand]. type DurationShorthandUnit string const ( @@ -108,7 +103,7 @@ var validDurationUnits = []DurationShorthandUnit{ DurationShorthandUnitWeek, } -// Validate checks if [DurationShorthand] is correct. +// Validate returns an error for an invalid duration shorthand. func (d DurationShorthand) Validate() error { return durationShortHandValidation.Validate(d) } diff --git a/pkg/openslo/v2alpha/objects.go b/pkg/openslo/v2alpha/objects.go index d5bd3c2..fde5730 100644 --- a/pkg/openslo/v2alpha/objects.go +++ b/pkg/openslo/v2alpha/objects.go @@ -10,6 +10,7 @@ import ( "github.com/OpenSLO/go-sdk/pkg/openslo" ) +// APIVersion is the OpenSLO v2alpha API version. const APIVersion = openslo.VersionV2alpha var supportedKinds = []openslo.Kind{ @@ -22,25 +23,37 @@ var supportedKinds = []openslo.Kind{ openslo.KindAlertNotificationTarget, } +// GetSupportedKinds returns a copy of the OpenSLO object kinds supported by v2alpha. func GetSupportedKinds() []openslo.Kind { return slices.Clone(supportedKinds) } +// Object is implemented by every OpenSLO v2alpha object and exposes its +// version-specific [Metadata]. type Object interface { openslo.Object + // GetMetadata returns the object's version-specific metadata. GetMetadata() Metadata } +// Metadata is the Kubernetes-style identifying metadata used by v2alpha +// objects. Unlike v1 object metadata, it does not contain a display name. type Metadata struct { - Name string `json:"name"` - Labels Labels `json:"labels,omitempty"` + // Name identifies the object when other OpenSLO objects refer to it. + Name string `json:"name"` + // Labels classifies the object with Kubernetes-style, single-valued labels. + Labels Labels `json:"labels,omitempty"` + // Annotations attaches non-identifying metadata with qualified keys. Annotations Annotations `json:"annotations,omitempty"` } +// Labels maps label keys to one string value each. type Labels map[string]string +// Annotations maps annotation keys to arbitrary string values. type Annotations map[string]string +// Operator specifies a comparison operation for an SLO objective or alert condition. type Operator string const ( @@ -62,6 +75,7 @@ var operatorValidation = govy.New( Rules(rules.OneOf(validOperators...)), ) +// Validate returns an error for an unsupported comparison operator. func (o Operator) Validate() error { return operatorValidation.Validate(o) } diff --git a/pkg/openslo/v2alpha/service.go b/pkg/openslo/v2alpha/service.go index 2bded63..850d87d 100644 --- a/pkg/openslo/v2alpha/service.go +++ b/pkg/openslo/v2alpha/service.go @@ -13,6 +13,7 @@ var ( _ = openslo.ObjectValidator[Service](Service{}) ) +// NewService returns a service from metadata and spec. func NewService(metadata Metadata, spec ServiceSpec) Service { return Service{ APIVersion: APIVersion, @@ -22,6 +23,7 @@ func NewService(metadata Metadata, spec ServiceSpec) Service { } } +// Service groups SLOs under a named service. type Service struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -29,35 +31,44 @@ type Service struct { Spec ServiceSpec `json:"spec"` } +// GetVersion returns [APIVersion]. func (s Service) GetVersion() openslo.Version { return APIVersion } +// GetKind returns [openslo.KindService]. func (s Service) GetKind() openslo.Kind { return openslo.KindService } +// GetName returns the service's metadata name. func (s Service) GetName() string { return s.Metadata.Name } +// Validate returns an error for an invalid service. func (s Service) Validate() error { return serviceValidation.Validate(s) } +// String returns the service's formatted version, kind, and name. func (s Service) String() string { return internal.GetObjectName(s) } +// GetMetadata returns the service's metadata. func (s Service) GetMetadata() Metadata { return s.Metadata } +// GetValidator returns the validator configured for [Service]. func (s Service) GetValidator() govy.Validator[Service] { return serviceValidation } +// ServiceSpec defines the descriptive attributes of a [Service]. type ServiceSpec struct { + // Description summarizes the service. Description string `json:"description,omitempty"` } diff --git a/pkg/openslo/v2alpha/sli.go b/pkg/openslo/v2alpha/sli.go index 043824b..0b99cf3 100644 --- a/pkg/openslo/v2alpha/sli.go +++ b/pkg/openslo/v2alpha/sli.go @@ -13,6 +13,7 @@ var ( _ = openslo.ObjectValidator[SLI](SLI{}) ) +// NewSLI returns an SLI from metadata and spec. func NewSLI(metadata Metadata, spec SLISpec) SLI { return SLI{ APIVersion: APIVersion, @@ -22,6 +23,10 @@ func NewSLI(metadata Metadata, spec SLISpec) SLI { } } +// SLI describes how to read a metric from a data source, as defined by the +// [OpenSLO v2alpha SLI]. +// +// [OpenSLO v2alpha SLI]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md#sli type SLI struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -29,49 +34,75 @@ type SLI struct { Spec SLISpec `json:"spec"` } +// GetVersion returns [APIVersion]. func (s SLI) GetVersion() openslo.Version { return APIVersion } +// GetKind returns [openslo.KindSLI]. func (s SLI) GetKind() openslo.Kind { return openslo.KindSLI } +// GetName returns the SLI's metadata name. func (s SLI) GetName() string { return s.Metadata.Name } +// Validate returns an error for an invalid SLI. func (s SLI) Validate() error { return sliValidation.Validate(s) } +// String returns the SLI's formatted version, kind, and name. func (s SLI) String() string { return internal.GetObjectName(s) } +// GetMetadata returns the SLI's metadata. func (s SLI) GetMetadata() Metadata { return s.Metadata } +// GetValidator returns the validator configured for [SLI]. func (s SLI) GetValidator() govy.Validator[SLI] { return sliValidation } +// SLISpec defines the metric used by an [SLI]. type SLISpec struct { - Description string `json:"description,omitempty"` - ThresholdMetric *SLIMetricSpec `json:"thresholdMetric,omitempty"` - RatioMetric *SLIRatioMetric `json:"ratioMetric,omitempty"` + // Description summarizes the indicator. + Description string `json:"description,omitempty"` + // ThresholdMetric supplies values for [SLOObjective.Operator] to compare with + // [SLOObjective.Value]. + ThresholdMetric *SLIMetricSpec `json:"thresholdMetric,omitempty"` + // RatioMetric supplies a success ratio for an SLO objective. + RatioMetric *SLIRatioMetric `json:"ratioMetric,omitempty"` } +// SLIRatioMetric derives a success ratio as good divided by total, total minus +// bad divided by total, or a precomputed raw ratio. +// For example, 990 good events out of 1,000 total events produce "0.99". +// 10 bad events with the same total produce the same success ratio. type SLIRatioMetric struct { - Counter bool `json:"counter"` - Good *SLIMetricSpec `json:"good,omitempty"` - Bad *SLIMetricSpec `json:"bad,omitempty"` - Total *SLIMetricSpec `json:"total,omitempty"` + // Counter reports whether the good, bad, and total metrics are monotonically + // increasing counters. It has no effect when Raw is used. + Counter bool `json:"counter"` + // Good is the success-count numerator used with Total. + Good *SLIMetricSpec `json:"good,omitempty"` + // Bad is the failure-count input used with Total to derive successes. + Bad *SLIMetricSpec `json:"bad,omitempty"` + // Total is the denominator paired with Good or Bad. + Total *SLIMetricSpec `json:"total,omitempty"` + // RawType identifies whether Raw contains a success or failure ratio when + // Raw is used. RawType SLIRawMetricType `json:"rawType,omitempty"` - Raw *SLIMetricSpec `json:"raw,omitempty"` + // Raw supplies an already computed ratio. + Raw *SLIMetricSpec `json:"raw,omitempty"` } +// SLIRawMetricType identifies whether a raw ratio contains successes +// (good/total) or failures (bad/total). type SLIRawMetricType string const ( @@ -84,10 +115,16 @@ var validSLIRawMetricTypes = []SLIRawMetricType{ SLIRawMetricTypeFailure, } +// SLIMetricSpec supplies a provider-specific query in the v2alpha flattened +// layout. type SLIMetricSpec struct { - DataSourceRef string `json:"dataSourceRef,omitempty"` + // DataSourceRef names an existing [DataSource]. + DataSourceRef string `json:"dataSourceRef,omitempty"` + // DataSourceSpec embeds the data-source configuration. DataSourceSpec *DataSourceSpec `json:"dataSourceSpec,omitempty"` - Spec map[string]any `json:"spec,omitempty"` + // Spec contains provider-specific query configuration at the same level as + // the data-source selection. + Spec map[string]any `json:"spec,omitempty"` } var sliValidation = govy.New( @@ -107,7 +144,8 @@ var sliSpecValidation = govy.New( Rules(rules.MutuallyExclusive(true, map[string]func(s SLISpec) any{ "thresholdMetric": func(s SLISpec) any { return s.ThresholdMetric }, "ratioMetric": func(s SLISpec) any { return s.RatioMetric }, - })), + }). + WithDescription("exactly one of 'thresholdMetric' and 'ratioMetric' must be set")), govy.ForPointer(func(spec SLISpec) *SLIMetricSpec { return spec.ThresholdMetric }). WithName("thresholdMetric"). Include(sliMetricSpecValidation), @@ -122,7 +160,8 @@ var sliRatioMetricValidation = govy.New( Rules(rules.MutuallyExclusive(true, map[string]func(m SLIRatioMetric) any{ "total": func(m SLIRatioMetric) any { return m.Total }, "raw": func(m SLIRatioMetric) any { return m.Raw }, - })). + }). + WithDescription("exactly one of 'total' and 'raw' must be set")). Rules(rules.MutuallyExclusive(false, map[string]func(m SLIRatioMetric) any{ "raw": func(m SLIRatioMetric) any { return m.Raw }, "good": func(m SLIRatioMetric) any { return m.Good }, @@ -145,16 +184,25 @@ var sliFractionMetricValidation = govy.New( govy.ForPointer(func(m SLIRatioMetric) *SLIMetricSpec { return m.Good }). WithName("good"). Cascade(govy.CascadeModeContinue). - When(func(m SLIRatioMetric) bool { return m.Good != nil }). + When( + func(m SLIRatioMetric) bool { return m.Good != nil }, + govy.WhenDescription("'good' is set"), + ). Include(sliMetricSpecValidation), govy.ForPointer(func(m SLIRatioMetric) *SLIMetricSpec { return m.Bad }). WithName("bad"). Cascade(govy.CascadeModeContinue). - When(func(m SLIRatioMetric) bool { return m.Bad != nil }). + When( + func(m SLIRatioMetric) bool { return m.Bad != nil }, + govy.WhenDescription("'bad' is set"), + ). Include(sliMetricSpecValidation), ). Cascade(govy.CascadeModeStop). - When(func(m SLIRatioMetric) bool { return m.Total != nil }) + When( + func(m SLIRatioMetric) bool { return m.Total != nil }, + govy.WhenDescription("'total' is set"), + ) var sliRawMetricSpecValidation = govy.New( govy.ForPointer(func(m SLIRatioMetric) *SLIMetricSpec { return m.Raw }). @@ -165,14 +213,18 @@ var sliRawMetricSpecValidation = govy.New( Required(). Rules(rules.OneOf(validSLIRawMetricTypes...)), ). - When(func(m SLIRatioMetric) bool { return m.Raw != nil }) + When( + func(m SLIRatioMetric) bool { return m.Raw != nil }, + govy.WhenDescription("'raw' is set"), + ) var sliMetricSpecValidation = govy.New( govy.For(govy.GetSelf[SLIMetricSpec]()). Rules(rules.MutuallyExclusive(true, map[string]func(s SLIMetricSpec) any{ "dataSourceRef": func(s SLIMetricSpec) any { return s.DataSourceRef }, "dataSourceSpec": func(s SLIMetricSpec) any { return s.DataSourceSpec }, - })), + }). + WithDescription("exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set")), govy.For(func(spec SLIMetricSpec) string { return spec.DataSourceRef }). WithName("dataSourceRef"). OmitEmpty(). diff --git a/pkg/openslo/v2alpha/sli_test.go b/pkg/openslo/v2alpha/sli_test.go index 8b282d7..bf41718 100644 --- a/pkg/openslo/v2alpha/sli_test.go +++ b/pkg/openslo/v2alpha/sli_test.go @@ -63,6 +63,42 @@ func TestSLI_Validate_Spec(t *testing.T) { }) } +func TestSLI_ValidationPlan(t *testing.T) { + plan, err := govy.Plan(sliValidation, govy.PlanStrictMode()) + assert.Require(t, assert.NoError(t, err)) + + for _, test := range []struct { + path string + description string + }{ + { + path: "$.spec", + description: "exactly one of 'thresholdMetric' and 'ratioMetric' must be set", + }, + { + path: "$.spec.ratioMetric", + description: "exactly one of 'total' and 'raw' must be set", + }, + { + path: "$.spec.thresholdMetric", + description: "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", + }, + } { + assertValidationPlanRule(t, plan, test.path, test.description) + } + for _, test := range []struct { + path string + conditions []string + }{ + {path: "$.spec.ratioMetric.good", conditions: []string{"'total' is set", "'good' is set"}}, + {path: "$.spec.ratioMetric.bad", conditions: []string{"'total' is set", "'bad' is set"}}, + {path: "$.spec.ratioMetric.total", conditions: []string{"'total' is set"}}, + {path: "$.spec.ratioMetric.raw", conditions: []string{"'raw' is set"}}, + } { + assertValidationPlanRule(t, plan, test.path, "property is optional", test.conditions...) + } +} + func runSLISpecTests[T openslo.Object](t *testing.T, path string, objectGetter func(SLISpec) T) { t.Helper() diff --git a/pkg/openslo/v2alpha/slo.go b/pkg/openslo/v2alpha/slo.go index 896c0a3..c892c8a 100644 --- a/pkg/openslo/v2alpha/slo.go +++ b/pkg/openslo/v2alpha/slo.go @@ -16,6 +16,7 @@ var ( _ = openslo.ObjectValidator[SLO](SLO{}) ) +// NewSLO returns an SLO from metadata and spec. func NewSLO(metadata Metadata, spec SLOSpec) SLO { return SLO{ APIVersion: APIVersion, @@ -25,6 +26,10 @@ func NewSLO(metadata Metadata, spec SLOSpec) SLO { } } +// SLO defines a target for an SLI over a time window, as specified by the +// [OpenSLO v2alpha SLO]. +// +// [OpenSLO v2alpha SLO]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md#slo type SLO struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -32,49 +37,74 @@ type SLO struct { Spec SLOSpec `json:"spec"` } +// GetVersion returns [APIVersion]. func (s SLO) GetVersion() openslo.Version { return APIVersion } +// GetKind returns [openslo.KindSLO]. func (s SLO) GetKind() openslo.Kind { return openslo.KindSLO } +// GetName returns the SLO's metadata name. func (s SLO) GetName() string { return s.Metadata.Name } +// Validate returns an error for an invalid SLO. func (s SLO) Validate() error { return sloValidation.Validate(s) } +// String returns the SLO's formatted version, kind, and name. func (s SLO) String() string { return internal.GetObjectName(s) } +// GetMetadata returns the SLO's metadata. func (s SLO) GetMetadata() Metadata { return s.Metadata } +// IsComposite reports whether at least one objective selects its own SLI. func (s SLO) IsComposite() bool { return s.Spec.HasCompositeObjectives() } +// GetValidator returns the validator configured for [SLO]. func (s SLO) GetValidator() govy.Validator[SLO] { return sloValidation } +// SLOSpec defines an SLO's service, SLI, time window, budgeting method, +// objectives, and alert policies. A standard SLO applies one SLI to all +// objectives. A composite SLO can select a different SLI per objective. type SLOSpec struct { - Description string `json:"description,omitempty"` - ServiceRef string `json:"serviceRef"` - SLI *SLOSLIInline `json:"sli,omitempty"` - SLIRef *string `json:"sliRef,omitempty"` + // Description summarizes the SLO. + Description string `json:"description,omitempty"` + // ServiceRef names the service associated with this SLO. The SDK serializes + // the field as "serviceRef". The living v2alpha proposal calls it "service". + ServiceRef string `json:"serviceRef"` + // SLI embeds the service level indicator for a standard SLO. + SLI *SLOSLIInline `json:"sli,omitempty"` + // SLIRef names an existing [SLI] for a standard SLO. + SLIRef *string `json:"sliRef,omitempty"` + // BudgetingMethod applies the selected error-budget calculation to every + // objective. BudgetingMethod SLOBudgetingMethod `json:"budgetingMethod"` - TimeWindow []SLOTimeWindow `json:"timeWindow,omitempty"` - Objectives []SLOObjective `json:"objectives"` - AlertPolicies []SLOAlertPolicy `json:"alertPolicies,omitempty"` + // TimeWindow defines the SLO evaluation period and its alignment. + TimeWindow []SLOTimeWindow `json:"timeWindow,omitempty"` + // Objectives contains the SLO's budget targets and metric thresholds. + // V2alpha permits multiple objectives for a threshold-metric SLO. + Objectives []SLOObjective `json:"objectives"` + // AlertPolicies contains inline or referenced policies associated with the SLO. + AlertPolicies []SLOAlertPolicy `json:"alertPolicies,omitempty"` } +// HasCompositeObjectives reports whether at least one objective selects an SLI +// inline or by reference. It does not verify that every composite objective +// selects one. func (s SLOSpec) HasCompositeObjectives() bool { for i := range s.Objectives { if s.Objectives[i].SLI != nil || s.Objectives[i].SLIRef != nil { @@ -84,6 +114,9 @@ func (s SLOSpec) HasCompositeObjectives() bool { return false } +// SLOBudgetingMethod selects how an SLO consumes its error budget. +// Occurrences uses good events over total events, Timeslices uses good slices +// over total slices, and RatioTimeslices averages slice success ratios. type SLOBudgetingMethod string const ( @@ -98,47 +131,81 @@ var validSLOBudgetingMethods = []SLOBudgetingMethod{ SLOBudgetingMethodRatioTimeslices, } +// SLOSLIInline embeds an SLI definition in an SLO or one of its objectives. type SLOSLIInline struct { Metadata Metadata `json:"metadata"` Spec SLISpec `json:"spec"` } +// SLOObjective defines one error-budget target and, for a threshold SLI, its +// metric comparison. The SDK validates threshold-specific fields only for an +// embedded SLI. A referenced SLI does not expose its metric type during +// validation. type SLOObjective struct { - DisplayName string `json:"displayName,omitempty"` - Operator Operator `json:"op,omitempty"` - Value *float64 `json:"value,omitempty"` - Target *float64 `json:"target,omitempty"` - TargetPercent *float64 `json:"targetPercent,omitempty"` - TimeSliceTarget *float64 `json:"timeSliceTarget,omitempty"` + // DisplayName is a human-readable name for this objective. + // It is not part of the enclosing object's [Metadata]. + DisplayName string `json:"displayName,omitempty"` + // Operator compares a threshold metric with Value. + Operator Operator `json:"op,omitempty"` + // Value is the comparison threshold for a threshold metric. + Value *float64 `json:"value,omitempty"` + // Target is the desired success proportion. For example, "0.995" means + // 99.5 percent. + Target *float64 `json:"target,omitempty"` + // TargetPercent is the desired success percentage. + TargetPercent *float64 `json:"targetPercent,omitempty"` + // TimeSliceTarget sets the per-slice success threshold for Timeslices. + TimeSliceTarget *float64 `json:"timeSliceTarget,omitempty"` + // TimeSliceWindow sets the size of each slice for Timeslices and + // RatioTimeslices. OpenSLO also accepts a number interpreted as minutes. This + // SDK represents only duration shorthand. TimeSliceWindow *DurationShorthand `json:"timeSliceWindow,omitempty"` - SLI *SLOSLIInline `json:"sli,omitempty"` - SLIRef *string `json:"sliRef,omitempty"` - CompositeWeight *float64 `json:"compositeWeight,omitempty"` + // SLI embeds this objective's service level indicator for a composite SLO. + SLI *SLOSLIInline `json:"sli,omitempty"` + // SLIRef names this objective's existing [SLI] for a composite SLO. + SLIRef *string `json:"sliRef,omitempty"` + // CompositeWeight scales this objective's contribution to a multi-objective + // composite SLO. OpenSLO uses a weight of 1 when omitted. This SDK leaves the + // field unset. + CompositeWeight *float64 `json:"compositeWeight,omitempty"` } +// SLOTimeWindow describes one rolling or calendar-aligned SLO evaluation window. type SLOTimeWindow struct { - Duration DurationShorthand `json:"duration"` - IsRolling bool `json:"isRolling"` - Calendar *SLOCalendar `json:"calendar,omitempty"` + // Duration is the length of the evaluation window. + Duration DurationShorthand `json:"duration"` + // IsRolling selects a rolling window when true and a calendar-aligned window + // when false. + IsRolling bool `json:"isRolling"` + // Calendar defines the alignment of a calendar window. + Calendar *SLOCalendar `json:"calendar,omitempty"` } +// SLOCalendar defines the starting wall-clock time and time zone for a +// calendar-aligned [SLOTimeWindow]. type SLOCalendar struct { + // StartTime is the local date and time when calendar alignment starts. StartTime string `json:"startTime"` - TimeZone string `json:"timeZone"` + // TimeZone determines how StartTime maps to an instant. + TimeZone string `json:"timeZone"` } +// SLOAlertPolicy associates an alert policy with an [SLO]. type SLOAlertPolicy struct { *SLOAlertPolicyInline *SLOAlertPolicyRef } +// SLOAlertPolicyInline is an alert-policy definition embedded in an SLO. type SLOAlertPolicyInline struct { Kind openslo.Kind `json:"kind"` Metadata Metadata `json:"metadata"` Spec AlertPolicySpec `json:"spec"` } +// SLOAlertPolicyRef identifies a separately defined [AlertPolicy]. type SLOAlertPolicyRef struct { + // AlertPolicyRef names the alert policy to use. AlertPolicyRef string `json:"alertPolicyRef"` } @@ -211,7 +278,7 @@ func getSLOSLIValidation[T any]( Rules(rules.MutuallyExclusive(true, map[string]func(t T) any{ "sli": func(t T) any { return sliGetter(t) }, "sliRef": func(t T) any { return sliRefGetter(t) }, - })), + }).WithDescription("exactly one of 'sli' and 'sliRef' must be set")), govy.ForPointer(sliGetter). WithName("sli"). Cascade(govy.CascadeModeContinue). @@ -227,7 +294,10 @@ func getSLOSLIValidation[T any]( ). // Another validation rule on 'spec' level already checks a scenario // in which neither 'sli' nor 'sliRef' are provided. - When(func(t T) bool { return sliGetter(t) != nil || sliRefGetter(t) != nil }). + When( + func(t T) bool { return sliGetter(t) != nil || sliRefGetter(t) != nil }, + govy.WhenDescription("'sli' or 'sliRef' is set"), + ). Cascade(govy.CascadeModeStop) } @@ -261,11 +331,11 @@ var sloTimeWindowValidation = govy.New( var sloAlertPolicyValidation = govy.New( govy.For(govy.GetSelf[SLOAlertPolicy]()). Rules(rules.MutuallyExclusive(true, map[string]func(a SLOAlertPolicy) any{ - "targetRef": func(a SLOAlertPolicy) any { return a.SLOAlertPolicyRef }, + "alertPolicyRef": func(a SLOAlertPolicy) any { return a.SLOAlertPolicyRef }, // It's impossible to list all fields that constitute the inlined version in the error message, // therefore 'spec' must suffice. "spec": func(a SLOAlertPolicy) any { return a.SLOAlertPolicyInline }, - })), + }).WithDescription("exactly one of 'alertPolicyRef' and 'spec' must be set")), govy.ForPointer(func(a SLOAlertPolicy) *SLOAlertPolicyRef { return a.SLOAlertPolicyRef }). @@ -299,7 +369,7 @@ var sloObjectiveValidation = govy.New( Rules(rules.MutuallyExclusive(true, map[string]func(o SLOObjective) any{ "target": func(o SLOObjective) any { return o.Target }, "targetPercent": func(o SLOObjective) any { return o.TargetPercent }, - })), + }).WithDescription("exactly one of 'target' and 'targetPercent' must be set")), govy.ForPointer(func(s SLOObjective) *float64 { return s.Target }). WithName("target"). Rules(rules.GTE(0.0), rules.LT(1.0)), @@ -353,7 +423,10 @@ var sloTimeSlicesObjectiveValidation = govy.New( validationRulesForTimeSliceWindow(), )), ). - When(func(s SLOSpec) bool { return s.BudgetingMethod == SLOBudgetingMethodTimeslices }) + When( + func(s SLOSpec) bool { return s.BudgetingMethod == SLOBudgetingMethodTimeslices }, + govy.WhenDescription("'budgetingMethod' is 'Timeslices'"), + ) var sloRatioTimeSlicesObjectiveValidation = govy.New( govy.ForSlice(func(spec SLOSpec) []SLOObjective { return spec.Objectives }). @@ -362,7 +435,10 @@ var sloRatioTimeSlicesObjectiveValidation = govy.New( validationRulesForTimeSliceWindow(), )), ). - When(func(s SLOSpec) bool { return s.BudgetingMethod == SLOBudgetingMethodRatioTimeslices }) + When( + func(s SLOSpec) bool { return s.BudgetingMethod == SLOBudgetingMethodRatioTimeslices }, + govy.WhenDescription("'budgetingMethod' is 'RatioTimeslices'"), + ) func validationRulesForTimeSliceWindow() govy.PropertyRules[DurationShorthand, SLOObjective] { return govy.ForPointer(func(s SLOObjective) *DurationShorthand { return s.TimeSliceWindow }). diff --git a/pkg/openslo/v2alpha/slo_test.go b/pkg/openslo/v2alpha/slo_test.go index b1a801f..b2b47a6 100644 --- a/pkg/openslo/v2alpha/slo_test.go +++ b/pkg/openslo/v2alpha/slo_test.go @@ -446,6 +446,17 @@ func TestSLO_Validate_Spec_AlertPolicies(t *testing.T) { err := slo.Validate() govytest.AssertError(t, err, govytest.ExpectedRuleError{ PropertyPath: "spec.alertPolicies[0]", + Message: "[alertPolicyRef, spec] properties are mutually exclusive, provide only one of them", + Code: rules.ErrorCodeMutuallyExclusive, + }) + }) + t.Run("neither ref nor inline is set", func(t *testing.T) { + slo := validRatioSLO() + slo.Spec.AlertPolicies[0] = SLOAlertPolicy{} + err := slo.Validate() + govytest.AssertError(t, err, govytest.ExpectedRuleError{ + PropertyPath: "spec.alertPolicies[0]", + Message: "one of [alertPolicyRef, spec] properties must be set, none was provided", Code: rules.ErrorCodeMutuallyExclusive, }) }) diff --git a/pkg/openslo/version.go b/pkg/openslo/version.go index 96099b6..c782540 100644 --- a/pkg/openslo/version.go +++ b/pkg/openslo/version.go @@ -11,6 +11,7 @@ const ( VersionV2alpha Version = "openslo.com/v2alpha" ) +// ParseVersion parses and validates an OpenSLO API version. func ParseVersion(s string) (Version, error) { version := Version(s) if err := version.Validate(); err != nil { @@ -19,10 +20,12 @@ func ParseVersion(s string) (Version, error) { return version, nil } +// String returns the serialized API version. func (v Version) String() string { return string(v) } +// Validate reports whether v is a supported API version. func (v Version) Validate() error { switch v { case VersionV1alpha, From c241af96f1127ab9fc68626498c2ed37896cbc97 Mon Sep 17 00:00:00 2001 From: Mateusz Hawrus Date: Mon, 17 Aug 2026 22:35:39 +0200 Subject: [PATCH 2/6] docs: clarify API comments and update documentation tooling Correct generated API descriptions and validation examples, and upgrade govy and related Go dependencies. --- docs/manifest.json | 62 +++++++++++------------ go.mod | 8 +-- go.sum | 16 +++--- internal/cmd/objectdoc/go.mod | 8 +-- internal/cmd/objectdoc/go.sum | 16 +++--- pkg/openslo/kind.go | 2 +- pkg/openslo/v1/alert_condition.go | 4 +- pkg/openslo/v1/alert_policy.go | 2 +- pkg/openslo/v1/slo.go | 7 +-- pkg/openslo/v2alpha/alert_condition.go | 4 +- pkg/openslo/v2alpha/alert_policy.go | 2 +- pkg/openslo/v2alpha/duration_shorthand.go | 7 +-- pkg/openslo/v2alpha/sli.go | 2 +- pkg/openslo/v2alpha/slo.go | 2 +- pkg/openslo/version.go | 2 +- 15 files changed, 73 insertions(+), 71 deletions(-) diff --git a/docs/manifest.json b/docs/manifest.json index ea140d9..10782b7 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -373,7 +373,7 @@ } ], "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", - "fieldDoc": "AlertAfter sets how long the comparison must hold before the condition breaches. OpenSLO defaults omitted values to \"0m\". This SDK does not apply that default." + "fieldDoc": "AlertAfter sets how long the condition must remain valid before an alert is triggered. OpenSLO defaults omitted values to \"0m\". This SDK does not apply that default." }, { "path": "$.spec.description", @@ -905,7 +905,7 @@ "name": "bool", "kind": "bool" }, - "fieldDoc": "AlertWhenBreaching enables notifications when the condition starts breaching." + "fieldDoc": "AlertWhenBreaching enables notifications when the condition is breaching." }, { "path": "$.spec.alertWhenResolved", @@ -1315,7 +1315,7 @@ } ], "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", - "fieldDoc": "AlertAfter sets how long the comparison must hold before the condition breaches. OpenSLO defaults omitted values to \"0m\". This SDK does not apply that default." + "fieldDoc": "AlertAfter sets how long the condition must remain valid before an alert is triggered. OpenSLO defaults omitted values to \"0m\". This SDK does not apply that default." }, { "path": "$.spec.conditions[*].spec.description", @@ -2303,7 +2303,7 @@ ] } ], - "typeDoc": "SLIRatioMetric derives a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 990 good events out of 1,000 total events produce \"0.99\". 10 bad events with the same total produce the same success ratio.", + "typeDoc": "SLIRatioMetric derives a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 990 good events out of 1,000 total events produce 0.99. 10 bad events with the same total produce the same success ratio.", "fieldDoc": "RatioMetric supplies a success ratio for an SLO objective.", "childrenPaths": [ "$.spec.ratioMetric.counter", @@ -3768,7 +3768,7 @@ ] } ], - "typeDoc": "SLIRatioMetric derives a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 990 good events out of 1,000 total events produce \"0.99\". 10 bad events with the same total produce the same success ratio.", + "typeDoc": "SLIRatioMetric derives a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 990 good events out of 1,000 total events produce 0.99. 10 bad events with the same total produce the same success ratio.", "fieldDoc": "RatioMetric supplies a success ratio for an SLO objective.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.counter", @@ -4852,7 +4852,7 @@ "errorCode": "less_than" } ], - "fieldDoc": "Target is the desired success proportion. For example, \"0.995\" means 99.5 percent." + "fieldDoc": "Target is the desired success proportion. For example, 0.995 means 99.5 percent." }, { "path": "$.spec.objectives[*].targetPercent", @@ -5402,7 +5402,7 @@ ] } ], - "typeDoc": "SLIRatioMetric derives a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 990 good events out of 1,000 total events produce \"0.99\". 10 bad events with the same total produce the same success ratio.", + "typeDoc": "SLIRatioMetric derives a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 990 good events out of 1,000 total events produce 0.99. 10 bad events with the same total produce the same success ratio.", "fieldDoc": "RatioMetric supplies a success ratio for an SLO objective.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.counter", @@ -5436,7 +5436,7 @@ "is composite SLO", "'sli' or 'sliRef' is set", "'total' is set", - "'bad' is set" + "'good' is set" ] }, { @@ -5446,7 +5446,7 @@ "is composite SLO", "'sli' or 'sliRef' is set", "'total' is set", - "'bad' is set" + "'good' is set" ] } ], @@ -5472,7 +5472,7 @@ "is composite SLO", "'sli' or 'sliRef' is set", "'total' is set", - "'bad' is set" + "'good' is set" ] }, { @@ -5482,7 +5482,7 @@ "is composite SLO", "'sli' or 'sliRef' is set", "'total' is set", - "'bad' is set" + "'good' is set" ] }, { @@ -5493,7 +5493,7 @@ "is composite SLO", "'sli' or 'sliRef' is set", "'total' is set", - "'bad' is set" + "'good' is set" ], "examples": [ "my-name", @@ -5518,7 +5518,7 @@ "is composite SLO", "'sli' or 'sliRef' is set", "'total' is set", - "'bad' is set" + "'good' is set" ] } ], @@ -5544,7 +5544,7 @@ "is composite SLO", "'sli' or 'sliRef' is set", "'total' is set", - "'bad' is set" + "'good' is set" ] } ], @@ -5564,7 +5564,7 @@ "is composite SLO", "'sli' or 'sliRef' is set", "'total' is set", - "'bad' is set" + "'good' is set" ] } ], @@ -5585,7 +5585,7 @@ "is composite SLO", "'sli' or 'sliRef' is set", "'total' is set", - "'bad' is set" + "'good' is set" ] } ], @@ -6540,7 +6540,7 @@ "name": "bool", "kind": "bool" }, - "fieldDoc": "AlertWhenBreaching enables notifications when the condition starts breaching." + "fieldDoc": "AlertWhenBreaching enables notifications when the condition is breaching." }, { "path": "$.spec.alertPolicies[*].spec.alertWhenResolved", @@ -6950,7 +6950,7 @@ } ], "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", - "fieldDoc": "AlertAfter sets how long the comparison must hold before the condition breaches. OpenSLO defaults omitted values to \"0m\". This SDK does not apply that default." + "fieldDoc": "AlertAfter sets how long the condition must remain valid before an alert is triggered. OpenSLO defaults omitted values to \"0m\". This SDK does not apply that default." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.description", @@ -7894,7 +7894,7 @@ } ], "typeDoc": "DurationShorthand represents a duration as an integer and a case-sensitive [DurationShorthandUnit](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DurationShorthandUnit), such as \"1m\" or \"10d\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", - "fieldDoc": "AlertAfter sets how long the comparison must hold before the condition breaches. OpenSLO treats an omitted value as \"0m\". This SDK leaves it unset." + "fieldDoc": "AlertAfter sets how long the condition must remain valid before an alert is triggered. OpenSLO treats an omitted value as \"0m\". This SDK leaves it unset." }, { "path": "$.spec.description", @@ -8428,7 +8428,7 @@ "name": "bool", "kind": "bool" }, - "fieldDoc": "AlertWhenBreaching enables alerts when the condition starts breaching." + "fieldDoc": "AlertWhenBreaching enables alerts when the condition is breaching." }, { "path": "$.spec.alertWhenResolved", @@ -8842,7 +8842,7 @@ } ], "typeDoc": "DurationShorthand represents a duration as an integer and a case-sensitive [DurationShorthandUnit](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DurationShorthandUnit), such as \"1m\" or \"10d\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", - "fieldDoc": "AlertAfter sets how long the comparison must hold before the condition breaches. OpenSLO treats an omitted value as \"0m\". This SDK leaves it unset." + "fieldDoc": "AlertAfter sets how long the condition must remain valid before an alert is triggered. OpenSLO treats an omitted value as \"0m\". This SDK leaves it unset." }, { "path": "$.spec.conditions[*].spec.description", @@ -11756,7 +11756,7 @@ "errorCode": "one_of" } ], - "typeDoc": "SLOBudgetingMethod identifies how an [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO) calculates objective success and error-budget use. Occurrences counts good events. Timeslices counts slices that meet [SLOObjective.TimeSliceTarget](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective.TimeSliceTarget). RatioTimeslices averages success ratios across slices. Composite SLOs apply objective weights to each calculation.", + "typeDoc": "SLOBudgetingMethod identifies how an [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO) calculates objective success and error-budget use. Occurrences uses the ratio of good events to total events. Timeslices counts slices that meet [SLOObjective.TimeSliceTarget](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective.TimeSliceTarget). RatioTimeslices averages success ratios across slices. Composite SLOs apply objective weights to each calculation.", "fieldDoc": "BudgetingMethod applies the selected error-budget calculation to every objective." }, { @@ -12548,7 +12548,7 @@ "is composite SLO", "'indicator' or 'indicatorRef' is set", "'total' is set", - "'bad' is set" + "'good' is set" ] } ], @@ -12573,7 +12573,7 @@ "is composite SLO", "'indicator' or 'indicatorRef' is set", "'total' is set", - "'bad' is set" + "'good' is set" ] } ], @@ -12598,7 +12598,7 @@ "is composite SLO", "'indicator' or 'indicatorRef' is set", "'total' is set", - "'bad' is set" + "'good' is set" ] }, { @@ -12608,7 +12608,7 @@ "is composite SLO", "'indicator' or 'indicatorRef' is set", "'total' is set", - "'bad' is set" + "'good' is set" ] }, { @@ -12619,7 +12619,7 @@ "is composite SLO", "'indicator' or 'indicatorRef' is set", "'total' is set", - "'bad' is set" + "'good' is set" ], "examples": [ "my-name", @@ -12651,7 +12651,7 @@ "is composite SLO", "'indicator' or 'indicatorRef' is set", "'total' is set", - "'bad' is set" + "'good' is set" ] }, { @@ -12661,7 +12661,7 @@ "is composite SLO", "'indicator' or 'indicatorRef' is set", "'total' is set", - "'bad' is set" + "'good' is set" ] } ], @@ -13484,7 +13484,7 @@ "name": "bool", "kind": "bool" }, - "fieldDoc": "AlertWhenBreaching enables alerts when the condition starts breaching." + "fieldDoc": "AlertWhenBreaching enables alerts when the condition is breaching." }, { "path": "$.spec.alertPolicies[*].spec.alertWhenResolved", @@ -13898,7 +13898,7 @@ } ], "typeDoc": "DurationShorthand represents a duration as an integer and a case-sensitive [DurationShorthandUnit](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DurationShorthandUnit), such as \"1m\" or \"10d\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", - "fieldDoc": "AlertAfter sets how long the comparison must hold before the condition breaches. OpenSLO treats an omitted value as \"0m\". This SDK leaves it unset." + "fieldDoc": "AlertAfter sets how long the condition must remain valid before an alert is triggered. OpenSLO treats an omitted value as \"0m\". This SDK leaves it unset." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.description", diff --git a/go.mod b/go.mod index f293758..2fbae0d 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,14 @@ module github.com/OpenSLO/go-sdk go 1.26 require ( - github.com/nobl9/govy v0.28.0 + github.com/nobl9/govy v0.29.0 sigs.k8s.io/yaml v1.6.0 ) require ( go.yaml.in/yaml/v2 v2.4.2 // indirect - golang.org/x/mod v0.38.0 // indirect + golang.org/x/mod v0.39.0 // indirect golang.org/x/sync v0.22.0 // indirect - golang.org/x/text v0.38.0 // indirect - golang.org/x/tools v0.48.0 // indirect + golang.org/x/text v0.41.0 // indirect + golang.org/x/tools v0.49.0 // indirect ) diff --git a/go.sum b/go.sum index 3c66cc1..87ce67a 100644 --- a/go.sum +++ b/go.sum @@ -1,19 +1,19 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/nobl9/govy v0.28.0 h1:NXHLoC90hOYxPpQxIcTME+/ju1K9vmW9NX34JsYKwTY= -github.com/nobl9/govy v0.28.0/go.mod h1:oJoCG++sQFOgE/GgAhmNasYfRoX6W+Mw+6rp9Dsxf34= +github.com/nobl9/govy v0.29.0 h1:djB8Tx6mYDYNhfrmuKsdMNuSQTCkZcLB3S87ygtZZhA= +github.com/nobl9/govy v0.29.0/go.mod h1:OdNFJceDL4MqQoQf9Z55yhHDTPs/4tDxmCw/Q7qzay8= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= -golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= -golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= +golang.org/x/mod v0.39.0 h1:UF5zwQdCRRUpHfyPwr7d4UrGiVeldIsogtzWVnczL74= +golang.org/x/mod v0.39.0/go.mod h1:bvIbwjQ0HUFFf5AKukeeYQG4ZBUG9yxQbR9aEweIwYY= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= -golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= -golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= -golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= +golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= +golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= diff --git a/internal/cmd/objectdoc/go.mod b/internal/cmd/objectdoc/go.mod index ee6105b..987c3ff 100644 --- a/internal/cmd/objectdoc/go.mod +++ b/internal/cmd/objectdoc/go.mod @@ -5,17 +5,17 @@ go 1.26 require ( github.com/OpenSLO/go-sdk v0.8.0 github.com/nieomylnieja/govydoc v0.0.1 - github.com/nobl9/govy v0.28.0 + github.com/nobl9/govy v0.29.0 github.com/stretchr/testify v1.11.1 - golang.org/x/tools v0.48.0 + golang.org/x/tools v0.49.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/mod v0.38.0 // indirect + golang.org/x/mod v0.39.0 // indirect golang.org/x/sync v0.22.0 // indirect - golang.org/x/text v0.38.0 // indirect + golang.org/x/text v0.41.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/internal/cmd/objectdoc/go.sum b/internal/cmd/objectdoc/go.sum index 2854174..ba7028b 100644 --- a/internal/cmd/objectdoc/go.sum +++ b/internal/cmd/objectdoc/go.sum @@ -4,22 +4,22 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/nieomylnieja/govydoc v0.0.1 h1:rhZYcn74X+axvjMQ7uxQxE/rCv+Zf39Fk6oplmoo2gc= github.com/nieomylnieja/govydoc v0.0.1/go.mod h1:xqtTVR8jflzkg3uXZeg0XzoOz9S/0x8XUnytsQ1dCiw= -github.com/nobl9/govy v0.28.0 h1:NXHLoC90hOYxPpQxIcTME+/ju1K9vmW9NX34JsYKwTY= -github.com/nobl9/govy v0.28.0/go.mod h1:oJoCG++sQFOgE/GgAhmNasYfRoX6W+Mw+6rp9Dsxf34= +github.com/nobl9/govy v0.29.0 h1:djB8Tx6mYDYNhfrmuKsdMNuSQTCkZcLB3S87ygtZZhA= +github.com/nobl9/govy v0.29.0/go.mod h1:OdNFJceDL4MqQoQf9Z55yhHDTPs/4tDxmCw/Q7qzay8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= -golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= -golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= +golang.org/x/mod v0.39.0 h1:UF5zwQdCRRUpHfyPwr7d4UrGiVeldIsogtzWVnczL74= +golang.org/x/mod v0.39.0/go.mod h1:bvIbwjQ0HUFFf5AKukeeYQG4ZBUG9yxQbR9aEweIwYY= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= -golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= -golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= -golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= +golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= +golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/pkg/openslo/kind.go b/pkg/openslo/kind.go index 477c4f2..c26925b 100644 --- a/pkg/openslo/kind.go +++ b/pkg/openslo/kind.go @@ -32,7 +32,7 @@ func (k Kind) String() string { return string(k) } -// Validate reports whether k is a supported object kind. +// Validate returns an error if k is not a supported object kind. func (k Kind) Validate() error { switch k { case KindSLO, diff --git a/pkg/openslo/v1/alert_condition.go b/pkg/openslo/v1/alert_condition.go index c3d0961..d7fb737 100644 --- a/pkg/openslo/v1/alert_condition.go +++ b/pkg/openslo/v1/alert_condition.go @@ -88,8 +88,8 @@ type AlertConditionType struct { Threshold *float64 `json:"threshold"` // LookbackWindow sets the period for burn-rate calculation. LookbackWindow DurationShorthand `json:"lookbackWindow"` - // AlertAfter sets how long the comparison must hold before the condition - // breaches. OpenSLO treats an omitted value as "0m". This SDK leaves it unset. + // AlertAfter sets how long the condition must remain valid before an alert is + // triggered. OpenSLO treats an omitted value as "0m". This SDK leaves it unset. AlertAfter *DurationShorthand `json:"alertAfter,omitempty"` } diff --git a/pkg/openslo/v1/alert_policy.go b/pkg/openslo/v1/alert_policy.go index e16537b..1fcda1f 100644 --- a/pkg/openslo/v1/alert_policy.go +++ b/pkg/openslo/v1/alert_policy.go @@ -77,7 +77,7 @@ type AlertPolicySpec struct { // AlertWhenNoData enables alerts when the associated SLO has no burn-rate // value. AlertWhenNoData bool `json:"alertWhenNoData,omitempty"` - // AlertWhenBreaching enables alerts when the condition starts breaching. + // AlertWhenBreaching enables alerts when the condition is breaching. AlertWhenBreaching bool `json:"alertWhenBreaching,omitempty"` // AlertWhenResolved enables alerts when the condition resolves. AlertWhenResolved bool `json:"alertWhenResolved,omitempty"` diff --git a/pkg/openslo/v1/slo.go b/pkg/openslo/v1/slo.go index 752b8df..a951c8d 100644 --- a/pkg/openslo/v1/slo.go +++ b/pkg/openslo/v1/slo.go @@ -114,9 +114,10 @@ func (s SLOSpec) HasCompositeObjectives() bool { // SLOBudgetingMethod identifies how an [SLO] calculates objective success and // error-budget use. -// Occurrences counts good events. Timeslices counts slices that meet -// [SLOObjective.TimeSliceTarget]. RatioTimeslices averages success ratios across -// slices. Composite SLOs apply objective weights to each calculation. +// Occurrences uses the ratio of good events to total events. Timeslices counts +// slices that meet [SLOObjective.TimeSliceTarget]. RatioTimeslices averages +// success ratios across slices. Composite SLOs apply objective weights to each +// calculation. type SLOBudgetingMethod string const ( diff --git a/pkg/openslo/v2alpha/alert_condition.go b/pkg/openslo/v2alpha/alert_condition.go index c4d6c05..221d068 100644 --- a/pkg/openslo/v2alpha/alert_condition.go +++ b/pkg/openslo/v2alpha/alert_condition.go @@ -88,8 +88,8 @@ type AlertConditionType struct { Threshold *float64 `json:"threshold"` // LookbackWindow sets the period for burn-rate calculation. LookbackWindow DurationShorthand `json:"lookbackWindow"` - // AlertAfter sets how long the comparison must hold before the condition - // breaches. OpenSLO defaults omitted values to "0m". This SDK does not apply + // AlertAfter sets how long the condition must remain valid before an alert is + // triggered. OpenSLO defaults omitted values to "0m". This SDK does not apply // that default. AlertAfter DurationShorthand `json:"alertAfter"` } diff --git a/pkg/openslo/v2alpha/alert_policy.go b/pkg/openslo/v2alpha/alert_policy.go index 96329d7..494ba70 100644 --- a/pkg/openslo/v2alpha/alert_policy.go +++ b/pkg/openslo/v2alpha/alert_policy.go @@ -74,7 +74,7 @@ type AlertPolicySpec struct { // AlertWhenNoData enables notifications when the associated SLO has no // burn-rate value. AlertWhenNoData bool `json:"alertWhenNoData,omitempty"` - // AlertWhenBreaching enables notifications when the condition starts breaching. + // AlertWhenBreaching enables notifications when the condition is breaching. AlertWhenBreaching bool `json:"alertWhenBreaching,omitempty"` // AlertWhenResolved enables notifications when the condition resolves. AlertWhenResolved bool `json:"alertWhenResolved,omitempty"` diff --git a/pkg/openslo/v2alpha/duration_shorthand.go b/pkg/openslo/v2alpha/duration_shorthand.go index 06a2198..5960432 100644 --- a/pkg/openslo/v2alpha/duration_shorthand.go +++ b/pkg/openslo/v2alpha/duration_shorthand.go @@ -8,7 +8,8 @@ import ( "github.com/nobl9/govy/pkg/rules" ) -// ParseDurationShorthand parses s into a [DurationShorthand]. +// ParseDurationShorthand parses s into a [DurationShorthand] without calling +// [DurationShorthand.Validate]. func ParseDurationShorthand(s string) (DurationShorthand, error) { d := new(DurationShorthand) err := d.UnmarshalText([]byte(s)) @@ -42,7 +43,7 @@ func (d *DurationShorthand) GetValue() int { return d.value } -// UnmarshalText decodes text into d as an [encoding.TextUnmarshaler]. +// UnmarshalText implements [encoding.TextUnmarshaler]. func (d *DurationShorthand) UnmarshalText(text []byte) error { if len(text) == 0 { return nil @@ -53,7 +54,7 @@ func (d *DurationShorthand) UnmarshalText(text []byte) error { return nil } -// MarshalText encodes d as an [encoding.TextMarshaler]. +// MarshalText implements [encoding.TextMarshaler]. func (d DurationShorthand) MarshalText() ([]byte, error) { if d.value == 0 { return []byte{}, nil diff --git a/pkg/openslo/v2alpha/sli.go b/pkg/openslo/v2alpha/sli.go index 0b99cf3..a4cd1fe 100644 --- a/pkg/openslo/v2alpha/sli.go +++ b/pkg/openslo/v2alpha/sli.go @@ -82,7 +82,7 @@ type SLISpec struct { // SLIRatioMetric derives a success ratio as good divided by total, total minus // bad divided by total, or a precomputed raw ratio. -// For example, 990 good events out of 1,000 total events produce "0.99". +// For example, 990 good events out of 1,000 total events produce 0.99. // 10 bad events with the same total produce the same success ratio. type SLIRatioMetric struct { // Counter reports whether the good, bad, and total metrics are monotonically diff --git a/pkg/openslo/v2alpha/slo.go b/pkg/openslo/v2alpha/slo.go index c892c8a..a590a23 100644 --- a/pkg/openslo/v2alpha/slo.go +++ b/pkg/openslo/v2alpha/slo.go @@ -149,7 +149,7 @@ type SLOObjective struct { Operator Operator `json:"op,omitempty"` // Value is the comparison threshold for a threshold metric. Value *float64 `json:"value,omitempty"` - // Target is the desired success proportion. For example, "0.995" means + // Target is the desired success proportion. For example, 0.995 means // 99.5 percent. Target *float64 `json:"target,omitempty"` // TargetPercent is the desired success percentage. diff --git a/pkg/openslo/version.go b/pkg/openslo/version.go index c782540..66324d0 100644 --- a/pkg/openslo/version.go +++ b/pkg/openslo/version.go @@ -25,7 +25,7 @@ func (v Version) String() string { return string(v) } -// Validate reports whether v is a supported API version. +// Validate returns an error if v is not a supported API version. func (v Version) Validate() error { switch v { case VersionV1alpha, From ce4128c1e3c208e43700ae262f4c72bc32390638 Mon Sep 17 00:00:00 2001 From: Mateusz Hawrus Date: Tue, 18 Aug 2026 09:14:31 +0200 Subject: [PATCH 3/6] test: remove validation plan tests Retain behavior-focused validation tests while removing brittle internal plan assertions. --- pkg/openslo/v1/alert_condition_test.go | 43 -------------- pkg/openslo/v1/alert_policy_test.go | 22 -------- pkg/openslo/v1/sli_test.go | 32 ----------- pkg/openslo/v1alpha/slo_test.go | 62 --------------------- pkg/openslo/v2alpha/alert_condition_test.go | 43 -------------- pkg/openslo/v2alpha/alert_policy_test.go | 22 -------- pkg/openslo/v2alpha/sli_test.go | 36 ------------ 7 files changed, 260 deletions(-) diff --git a/pkg/openslo/v1/alert_condition_test.go b/pkg/openslo/v1/alert_condition_test.go index 9e7fa8e..dfee861 100644 --- a/pkg/openslo/v1/alert_condition_test.go +++ b/pkg/openslo/v1/alert_condition_test.go @@ -1,11 +1,9 @@ package v1 import ( - "slices" "strings" "testing" - "github.com/nobl9/govy/pkg/govy" "github.com/nobl9/govy/pkg/govytest" "github.com/nobl9/govy/pkg/rules" @@ -55,19 +53,6 @@ func TestAlertCondition_Validate_Spec(t *testing.T) { }) } -func TestAlertCondition_ValidationPlan(t *testing.T) { - plan, err := govy.Plan(alertConditionValidation, govy.PlanStrictMode()) - assert.Require(t, assert.NoError(t, err)) - - assertValidationPlanRule( - t, - plan, - "$.spec.condition.op", - "must be one of: gt, lt, gte, lte", - "'kind' is 'burnrate'", - ) -} - func runAlertConditionSpecTests[T openslo.Object]( t *testing.T, path string, @@ -221,31 +206,3 @@ func validAlertCondition() AlertCondition { }, ) } - -func assertValidationPlanRule( - t *testing.T, - plan *govy.ValidatorPlan, - path string, - description string, - conditions ...string, -) { - t.Helper() - for _, property := range plan.Properties { - if property.Path.String() != path { - continue - } - for _, rule := range property.Rules { - if rule.Description != description { - continue - } - for _, condition := range conditions { - if !slices.Contains(rule.Conditions, condition) { - t.Errorf("validation rule %q at %s does not have condition %q", description, path, condition) - return - } - } - return - } - } - t.Errorf("validation plan does not contain rule %q at %s", description, path) -} diff --git a/pkg/openslo/v1/alert_policy_test.go b/pkg/openslo/v1/alert_policy_test.go index 6b04931..3ec22c4 100644 --- a/pkg/openslo/v1/alert_policy_test.go +++ b/pkg/openslo/v1/alert_policy_test.go @@ -5,7 +5,6 @@ import ( "strings" "testing" - "github.com/nobl9/govy/pkg/govy" "github.com/nobl9/govy/pkg/govytest" "github.com/nobl9/govy/pkg/rules" @@ -60,27 +59,6 @@ func TestAlertPolicy_Validate_Spec(t *testing.T) { }) } -func TestAlertPolicy_ValidationPlan(t *testing.T) { - plan, err := govy.Plan(alertPolicyValidation, govy.PlanStrictMode()) - assert.Require(t, assert.NoError(t, err)) - - for _, test := range []struct { - path string - description string - }{ - { - path: "$.spec.conditions[*]", - description: "exactly one of 'conditionRef' and 'spec' must be set", - }, - { - path: "$.spec.notificationTargets[*]", - description: "exactly one of 'targetRef' and 'spec' must be set", - }, - } { - assertValidationPlanRule(t, plan, test.path, test.description) - } -} - func runAlertPolicySpecTests[T openslo.Object]( t *testing.T, path string, diff --git a/pkg/openslo/v1/sli_test.go b/pkg/openslo/v1/sli_test.go index 96b6784..142f007 100644 --- a/pkg/openslo/v1/sli_test.go +++ b/pkg/openslo/v1/sli_test.go @@ -62,38 +62,6 @@ func TestSLI_Validate_Spec(t *testing.T) { }) } -func TestSLI_ValidationPlan(t *testing.T) { - plan, err := govy.Plan(sliValidation, govy.PlanStrictMode()) - assert.Require(t, assert.NoError(t, err)) - - for _, test := range []struct { - path string - description string - }{ - { - path: "$.spec", - description: "exactly one of 'thresholdMetric' and 'ratioMetric' must be set", - }, - { - path: "$.spec.ratioMetric", - description: "exactly one of 'total' and 'raw' must be set", - }, - } { - assertValidationPlanRule(t, plan, test.path, test.description) - } - for _, test := range []struct { - path string - conditions []string - }{ - {path: "$.spec.ratioMetric.good", conditions: []string{"'total' is set", "'good' is set"}}, - {path: "$.spec.ratioMetric.bad", conditions: []string{"'total' is set", "'bad' is set"}}, - {path: "$.spec.ratioMetric.total", conditions: []string{"'total' is set"}}, - {path: "$.spec.ratioMetric.raw", conditions: []string{"'raw' is set"}}, - } { - assertValidationPlanRule(t, plan, test.path, "property is optional", test.conditions...) - } -} - func runSLISpecTests[T openslo.Object](t *testing.T, path string, objectGetter func(SLISpec) T) { t.Helper() diff --git a/pkg/openslo/v1alpha/slo_test.go b/pkg/openslo/v1alpha/slo_test.go index 9c064a9..959c39e 100644 --- a/pkg/openslo/v1alpha/slo_test.go +++ b/pkg/openslo/v1alpha/slo_test.go @@ -2,7 +2,6 @@ package v1alpha import ( "fmt" - "slices" "strings" "testing" @@ -25,67 +24,6 @@ func TestSLO_Validate_Ok(t *testing.T) { } } -func TestSLO_ValidationPlan(t *testing.T) { - plan, err := govy.Plan(sloValidation, govy.PlanStrictMode()) - assert.Require(t, assert.NoError(t, err)) - - assertValidationPlanRule( - t, - plan, - "$.spec", - "exactly one of 'indicator' and 'objectives[*].ratioMetrics' must be set", - ) - assertValidationPlanRule( - t, - plan, - "$.spec.objectives[*].value", - "property is required", - "'ratioMetrics' is not set", - ) - assertValidationPlanRule( - t, - plan, - "$.spec.objectives[*].op", - "property is required", - "'ratioMetrics' is not set", - ) - assertValidationPlanRule( - t, - plan, - "$.spec.objectives[*].op", - "must be one of: gt, lt, gte, lte", - "'ratioMetrics' is not set", - ) -} - -func assertValidationPlanRule( - t *testing.T, - plan *govy.ValidatorPlan, - path string, - description string, - conditions ...string, -) { - t.Helper() - for _, property := range plan.Properties { - if property.Path.String() != path { - continue - } - for _, rule := range property.Rules { - if rule.Description != description { - continue - } - for _, condition := range conditions { - if !slices.Contains(rule.Conditions, condition) { - t.Errorf("validation rule %q at %s does not have condition %q", description, path, condition) - return - } - } - return - } - } - t.Errorf("validation plan does not contain rule %q at %s", description, path) -} - func TestSLO_Validate_VersionAndKind(t *testing.T) { slo := validSLO() slo.APIVersion = "v0.1" diff --git a/pkg/openslo/v2alpha/alert_condition_test.go b/pkg/openslo/v2alpha/alert_condition_test.go index 381a63c..9dde2a0 100644 --- a/pkg/openslo/v2alpha/alert_condition_test.go +++ b/pkg/openslo/v2alpha/alert_condition_test.go @@ -1,11 +1,9 @@ package v2alpha import ( - "slices" "strings" "testing" - "github.com/nobl9/govy/pkg/govy" "github.com/nobl9/govy/pkg/govytest" "github.com/nobl9/govy/pkg/rules" @@ -55,19 +53,6 @@ func TestAlertCondition_Validate_Spec(t *testing.T) { }) } -func TestAlertCondition_ValidationPlan(t *testing.T) { - plan, err := govy.Plan(alertConditionValidation, govy.PlanStrictMode()) - assert.Require(t, assert.NoError(t, err)) - - assertValidationPlanRule( - t, - plan, - "$.spec.condition.op", - "must be one of: gt, lt, gte, lte", - "'kind' is 'burnrate'", - ) -} - func runAlertConditionSpecTests[T openslo.Object]( t *testing.T, path string, @@ -225,31 +210,3 @@ func validAlertCondition() AlertCondition { }, ) } - -func assertValidationPlanRule( - t *testing.T, - plan *govy.ValidatorPlan, - path string, - description string, - conditions ...string, -) { - t.Helper() - for _, property := range plan.Properties { - if property.Path.String() != path { - continue - } - for _, rule := range property.Rules { - if rule.Description != description { - continue - } - for _, condition := range conditions { - if !slices.Contains(rule.Conditions, condition) { - t.Errorf("validation rule %q at %s does not have condition %q", description, path, condition) - return - } - } - return - } - } - t.Errorf("validation plan does not contain rule %q at %s", description, path) -} diff --git a/pkg/openslo/v2alpha/alert_policy_test.go b/pkg/openslo/v2alpha/alert_policy_test.go index 54ef1f1..48a1ef9 100644 --- a/pkg/openslo/v2alpha/alert_policy_test.go +++ b/pkg/openslo/v2alpha/alert_policy_test.go @@ -5,7 +5,6 @@ import ( "strings" "testing" - "github.com/nobl9/govy/pkg/govy" "github.com/nobl9/govy/pkg/govytest" "github.com/nobl9/govy/pkg/rules" @@ -60,27 +59,6 @@ func TestAlertPolicy_Validate_Spec(t *testing.T) { }) } -func TestAlertPolicy_ValidationPlan(t *testing.T) { - plan, err := govy.Plan(alertPolicyValidation, govy.PlanStrictMode()) - assert.Require(t, assert.NoError(t, err)) - - for _, test := range []struct { - path string - description string - }{ - { - path: "$.spec.conditions[*]", - description: "exactly one of 'conditionRef' and 'spec' must be set", - }, - { - path: "$.spec.notificationTargets[*]", - description: "exactly one of 'targetRef' and 'spec' must be set", - }, - } { - assertValidationPlanRule(t, plan, test.path, test.description) - } -} - func runAlertPolicySpecTests[T openslo.Object]( t *testing.T, path string, diff --git a/pkg/openslo/v2alpha/sli_test.go b/pkg/openslo/v2alpha/sli_test.go index bf41718..8b282d7 100644 --- a/pkg/openslo/v2alpha/sli_test.go +++ b/pkg/openslo/v2alpha/sli_test.go @@ -63,42 +63,6 @@ func TestSLI_Validate_Spec(t *testing.T) { }) } -func TestSLI_ValidationPlan(t *testing.T) { - plan, err := govy.Plan(sliValidation, govy.PlanStrictMode()) - assert.Require(t, assert.NoError(t, err)) - - for _, test := range []struct { - path string - description string - }{ - { - path: "$.spec", - description: "exactly one of 'thresholdMetric' and 'ratioMetric' must be set", - }, - { - path: "$.spec.ratioMetric", - description: "exactly one of 'total' and 'raw' must be set", - }, - { - path: "$.spec.thresholdMetric", - description: "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", - }, - } { - assertValidationPlanRule(t, plan, test.path, test.description) - } - for _, test := range []struct { - path string - conditions []string - }{ - {path: "$.spec.ratioMetric.good", conditions: []string{"'total' is set", "'good' is set"}}, - {path: "$.spec.ratioMetric.bad", conditions: []string{"'total' is set", "'bad' is set"}}, - {path: "$.spec.ratioMetric.total", conditions: []string{"'total' is set"}}, - {path: "$.spec.ratioMetric.raw", conditions: []string{"'raw' is set"}}, - } { - assertValidationPlanRule(t, plan, test.path, "property is optional", test.conditions...) - } -} - func runSLISpecTests[T openslo.Object](t *testing.T, path string, objectGetter func(SLISpec) T) { t.Helper() From eecd1d01985879c12bf31f756ab4dd7b10aeaf98 Mon Sep 17 00:00:00 2001 From: Mateusz Hawrus Date: Wed, 19 Aug 2026 16:47:09 +0200 Subject: [PATCH 4/6] docs: clarify v2alpha schema documentation Update generated manifest descriptions, optional-field rules, validation details, and implementation-specific behavior across alerting, data source, SLI, and ratio metric types. --- docs/manifest.json | 1789 +++++++++++------ go.work.sum | 4 +- internal/cmd/objectdoc/main_test.go | 111 +- pkg/openslo/v1/alert_condition.go | 18 +- pkg/openslo/v1/alert_notification_target.go | 11 +- pkg/openslo/v1/alert_policy.go | 70 +- pkg/openslo/v1/data_source.go | 17 +- pkg/openslo/v1/objects.go | 7 +- pkg/openslo/v1/service.go | 8 +- pkg/openslo/v1/sli.go | 29 +- pkg/openslo/v1/slo.go | 50 +- pkg/openslo/v1alpha/doc.go | 4 +- pkg/openslo/v1alpha/service.go | 10 +- pkg/openslo/v1alpha/slo.go | 31 +- pkg/openslo/v2alpha/alert_condition.go | 26 +- .../v2alpha/alert_notification_target.go | 24 +- pkg/openslo/v2alpha/alert_policy.go | 54 +- pkg/openslo/v2alpha/data_source.go | 26 +- pkg/openslo/v2alpha/objects.go | 8 +- pkg/openslo/v2alpha/service.go | 16 +- pkg/openslo/v2alpha/sli.go | 68 +- pkg/openslo/v2alpha/slo.go | 46 +- 22 files changed, 1573 insertions(+), 854 deletions(-) diff --git a/docs/manifest.json b/docs/manifest.json index 10782b7..c20f475 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -10,7 +10,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "typeDoc": "AlertCondition defines when an SLO alert is considered breaching.", + "typeDoc": "AlertCondition defines when an SLO alert condition is breaching. [AlertPolicySpec.AlertWhenBreaching](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicySpec.AlertWhenBreaching) controls whether that state triggers an alert.\n\nThis type is the SDK's v2alpha representation. The living, unstable [OpenSLO v2alpha proposal](https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md) does not define alert objects.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -116,8 +116,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Labels maps label keys to one string value each.", - "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", + "fieldDoc": "Labels optionally classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*" @@ -171,8 +177,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps annotation keys to arbitrary string values.", - "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", + "fieldDoc": "Annotations optionally attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -286,7 +298,7 @@ } ], "typeDoc": "AlertConditionKind identifies the evaluation algorithm for an [AlertConditionType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionType).", - "fieldDoc": "Kind selects the condition algorithm. OpenSLO defaults omitted values to [AlertConditionKindBurnRate](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionKindBurnRate). This SDK does not apply that default." + "fieldDoc": "Kind selects the condition algorithm. This SDK requires Kind and does not apply the OpenSLO v1 default of [AlertConditionKindBurnRate](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionKindBurnRate)." }, { "path": "$.spec.condition.op", @@ -373,7 +385,7 @@ } ], "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", - "fieldDoc": "AlertAfter sets how long the condition must remain valid before an alert is triggered. OpenSLO defaults omitted values to \"0m\". This SDK does not apply that default." + "fieldDoc": "AlertAfter sets how long the burn-rate comparison must remain true before the condition becomes breaching. This SDK requires AlertAfter and does not apply the OpenSLO v1 default of \"0m\"." }, { "path": "$.spec.description", @@ -382,12 +394,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the alert condition." + "fieldDoc": "Description optionally summarizes the alert condition in at most 1,050 characters." } ] }, @@ -401,7 +417,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "typeDoc": "AlertNotificationTarget names a destination for alert delivery.", + "typeDoc": "AlertNotificationTarget represents a destination for alert delivery. The consuming implementation defines the format of [AlertNotificationTargetSpec.Target](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertNotificationTargetSpec.Target).\n\nThis type is the SDK's v2alpha representation. The living, unstable [OpenSLO v2alpha proposal](https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md) does not define alert objects.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -507,8 +523,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Labels maps label keys to one string value each.", - "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", + "fieldDoc": "Labels optionally classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*" @@ -562,8 +584,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps annotation keys to arbitrary string values.", - "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", + "fieldDoc": "Annotations optionally attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -613,7 +641,7 @@ "errorCode": "required" } ], - "typeDoc": "AlertNotificationTargetSpec identifies the consumer-defined notification destination.", + "typeDoc": "AlertNotificationTargetSpec identifies a notification destination. The consuming implementation defines the required [AlertNotificationTargetSpec.Target](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertNotificationTargetSpec.Target) format.", "childrenPaths": [ "$.spec.description", "$.spec.target" @@ -626,12 +654,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the destination." + "fieldDoc": "Description optionally summarizes the target in at most 1,050 characters." }, { "path": "$.spec.target", @@ -645,7 +677,7 @@ "errorCode": "required" } ], - "fieldDoc": "Target identifies the consumer-defined destination for alert delivery." + "fieldDoc": "Target specifies the notification destination in the format that the consuming implementation requires. Examples include \"email\", \"slack\", \"web-hook\", and \"Opsgenie\"." } ] }, @@ -659,7 +691,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "typeDoc": "AlertPolicy defines when the system emits an SLO alert and where it sends it.", + "typeDoc": "AlertPolicy defines which alert-condition states trigger an SLO alert. It also defines the notification destinations for triggered alerts.\n\nThis type is the SDK's v2alpha representation. The living, unstable [OpenSLO v2alpha proposal](https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md) does not define alert objects.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -765,8 +797,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Labels maps label keys to one string value each.", - "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", + "fieldDoc": "Labels optionally classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*" @@ -820,8 +858,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps annotation keys to arbitrary string values.", - "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", + "fieldDoc": "Annotations optionally attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -865,7 +909,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "typeDoc": "AlertPolicySpec defines the trigger states, condition, and notification destinations for an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy).", + "typeDoc": "AlertPolicySpec defines the trigger states, condition, and notification destinations for an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy). The trigger flags are independent and have a false zero value. JSON encoding omits false values. This SDK applies no omission default and accepts all three flags as false.", "childrenPaths": [ "$.spec.description", "$.spec.alertWhenNoData", @@ -884,12 +928,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the alert policy." + "fieldDoc": "Description optionally summarizes the alert policy in at most 1,050 characters." }, { "path": "$.spec.alertWhenNoData", @@ -897,7 +945,13 @@ "name": "bool", "kind": "bool" }, - "fieldDoc": "AlertWhenNoData enables notifications when the associated SLO has no burn-rate value." + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "fieldDoc": "AlertWhenNoData controls whether a missing burn-rate value triggers an alert." }, { "path": "$.spec.alertWhenBreaching", @@ -905,7 +959,13 @@ "name": "bool", "kind": "bool" }, - "fieldDoc": "AlertWhenBreaching enables notifications when the condition is breaching." + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "fieldDoc": "AlertWhenBreaching controls whether a breaching condition triggers an alert." }, { "path": "$.spec.alertWhenResolved", @@ -913,7 +973,13 @@ "name": "bool", "kind": "bool" }, - "fieldDoc": "AlertWhenResolved enables notifications when the condition resolves." + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "fieldDoc": "AlertWhenResolved controls whether a resolved condition triggers an alert." }, { "path": "$.spec.conditions", @@ -928,7 +994,7 @@ "errorCode": "slice_length" } ], - "fieldDoc": "Conditions supplies the policy's alert condition by reference or inline." + "fieldDoc": "Conditions contains exactly one alert condition, specified inline or by reference." }, { "path": "$.spec.conditions[*]", @@ -947,7 +1013,7 @@ "errorCode": "optional" } ], - "typeDoc": "AlertPolicyCondition supplies a condition to an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy) by reference or inline definition.", + "typeDoc": "AlertPolicyCondition supplies exactly one condition to an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy). Set either the reference or the inline definition.", "childrenPaths": [ "$.spec.conditions[*].conditionRef", "$.spec.conditions[*].kind", @@ -980,7 +1046,7 @@ ] } ], - "fieldDoc": "ConditionRef names the alert condition to use." + "fieldDoc": "ConditionRef is the metadata name of the alert condition to use." }, { "path": "$.spec.conditions[*].kind", @@ -1058,8 +1124,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Labels maps label keys to one string value each.", - "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", + "fieldDoc": "Labels optionally classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.spec.conditions[*].metadata.labels.*~", "$.spec.conditions[*].metadata.labels.*" @@ -1113,8 +1185,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps annotation keys to arbitrary string values.", - "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", + "fieldDoc": "Annotations optionally attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.spec.conditions[*].metadata.annotations.*~", "$.spec.conditions[*].metadata.annotations.*" @@ -1228,7 +1306,7 @@ } ], "typeDoc": "AlertConditionKind identifies the evaluation algorithm for an [AlertConditionType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionType).", - "fieldDoc": "Kind selects the condition algorithm. OpenSLO defaults omitted values to [AlertConditionKindBurnRate](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionKindBurnRate). This SDK does not apply that default." + "fieldDoc": "Kind selects the condition algorithm. This SDK requires Kind and does not apply the OpenSLO v1 default of [AlertConditionKindBurnRate](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionKindBurnRate)." }, { "path": "$.spec.conditions[*].spec.condition.op", @@ -1315,7 +1393,7 @@ } ], "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", - "fieldDoc": "AlertAfter sets how long the condition must remain valid before an alert is triggered. OpenSLO defaults omitted values to \"0m\". This SDK does not apply that default." + "fieldDoc": "AlertAfter sets how long the burn-rate comparison must remain true before the condition becomes breaching. This SDK requires AlertAfter and does not apply the OpenSLO v1 default of \"0m\"." }, { "path": "$.spec.conditions[*].spec.description", @@ -1324,12 +1402,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the alert condition." + "fieldDoc": "Description optionally summarizes the alert condition in at most 1,050 characters." }, { "path": "$.spec.notificationTargets", @@ -1344,7 +1426,7 @@ "errorCode": "slice_min_length" } ], - "fieldDoc": "NotificationTargets lists referenced or inline delivery destinations." + "fieldDoc": "NotificationTargets contains one or more delivery destinations. Specify each destination inline or by reference." }, { "path": "$.spec.notificationTargets[*]", @@ -1363,7 +1445,7 @@ "errorCode": "optional" } ], - "typeDoc": "AlertPolicyNotificationTarget supplies a notification destination to an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy) by reference or inline definition.", + "typeDoc": "AlertPolicyNotificationTarget supplies exactly one notification destination to an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy). Set either the reference or the inline definition.", "childrenPaths": [ "$.spec.notificationTargets[*].targetRef", "$.spec.notificationTargets[*].kind", @@ -1396,7 +1478,7 @@ ] } ], - "fieldDoc": "TargetRef names the notification target to use." + "fieldDoc": "TargetRef is the metadata name of the notification target to use." }, { "path": "$.spec.notificationTargets[*].kind", @@ -1474,8 +1556,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Labels maps label keys to one string value each.", - "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", + "fieldDoc": "Labels optionally classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.spec.notificationTargets[*].metadata.labels.*~", "$.spec.notificationTargets[*].metadata.labels.*" @@ -1529,8 +1617,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps annotation keys to arbitrary string values.", - "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", + "fieldDoc": "Annotations optionally attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.spec.notificationTargets[*].metadata.annotations.*~", "$.spec.notificationTargets[*].metadata.annotations.*" @@ -1580,7 +1674,7 @@ "errorCode": "required" } ], - "typeDoc": "AlertNotificationTargetSpec identifies the consumer-defined notification destination.", + "typeDoc": "AlertNotificationTargetSpec identifies a notification destination. The consuming implementation defines the required [AlertNotificationTargetSpec.Target](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertNotificationTargetSpec.Target) format.", "childrenPaths": [ "$.spec.notificationTargets[*].spec.description", "$.spec.notificationTargets[*].spec.target" @@ -1593,12 +1687,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the destination." + "fieldDoc": "Description optionally summarizes the target in at most 1,050 characters." }, { "path": "$.spec.notificationTargets[*].spec.target", @@ -1612,7 +1710,7 @@ "errorCode": "required" } ], - "fieldDoc": "Target identifies the consumer-defined destination for alert delivery." + "fieldDoc": "Target specifies the notification destination in the format that the consuming implementation requires. Examples include \"email\", \"slack\", \"web-hook\", and \"Opsgenie\"." } ] }, @@ -1626,7 +1724,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "typeDoc": "DataSource makes metric-source connection details reusable across SLIs.", + "typeDoc": "DataSource represents reusable connection details for a metric source. [SLIMetricSpec.DataSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIMetricSpec.DataSourceRef) selects it by metadata name. A metric query can instead embed [SLIMetricSpec.DataSourceSpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIMetricSpec.DataSourceSpec). [SLIMetricSpec.Spec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIMetricSpec.Spec) contains implementation-defined query configuration.\n\nThis type is the SDK's v2alpha representation. The living, unstable [OpenSLO v2alpha proposal](https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md) does not define a standalone DataSource schema.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -1732,8 +1830,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Labels maps label keys to one string value each.", - "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", + "fieldDoc": "Labels optionally classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*" @@ -1787,8 +1891,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps annotation keys to arbitrary string values.", - "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", + "fieldDoc": "Annotations optionally attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -1838,7 +1948,7 @@ "errorCode": "required" } ], - "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", + "typeDoc": "DataSourceSpec defines a metric-source type and its implementation-defined connection data.", "childrenPaths": [ "$.spec.description", "$.spec.type", @@ -1852,12 +1962,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the data source." + "fieldDoc": "Description optionally summarizes the data source in at most 1,050 characters." }, { "path": "$.spec.type", @@ -1871,7 +1985,7 @@ "errorCode": "required" } ], - "fieldDoc": "Type identifies the data-source implementation understood by the consumer." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." }, { "path": "$.spec.connectionDetails", @@ -1886,7 +2000,7 @@ "errorCode": "required" } ], - "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON, such as endpoints or authentication settings." } ] }, @@ -1900,7 +2014,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "typeDoc": "SLI describes how to read a metric from a data source, as defined by the [OpenSLO v2alpha SLI](https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md#sli).", + "typeDoc": "SLI defines a derived reliability indicator calculated from one or more metric queries against data sources, as defined by the [OpenSLO v2alpha SLI](https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md#sli).", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -2006,8 +2120,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Labels maps label keys to one string value each.", - "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", + "fieldDoc": "Labels optionally classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*" @@ -2061,8 +2181,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps annotation keys to arbitrary string values.", - "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", + "fieldDoc": "Annotations optionally attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -2112,7 +2238,7 @@ "errorCode": "mutually_exclusive" } ], - "typeDoc": "SLISpec defines the metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLI).", + "typeDoc": "SLISpec defines the query or queries used to calculate an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLI).", "childrenPaths": [ "$.spec.description", "$.spec.thresholdMetric", @@ -2126,12 +2252,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the indicator." + "fieldDoc": "Description optionally summarizes the indicator in at most 1,050 characters." }, { "path": "$.spec.thresholdMetric", @@ -2150,8 +2280,8 @@ "errorCode": "mutually_exclusive" } ], - "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", - "fieldDoc": "ThresholdMetric supplies values for [SLOObjective.Operator](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Operator) to compare with [SLOObjective.Value](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Value).", + "typeDoc": "SLIMetricSpec supplies an implementation-defined query in the v2alpha flattened layout.", + "fieldDoc": "ThresholdMetric defines a query that returns values for comparison with [SLOObjective.Value](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Value) by [SLOObjective.Operator](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Operator).", "childrenPaths": [ "$.spec.thresholdMetric.dataSourceRef", "$.spec.thresholdMetric.dataSourceSpec", @@ -2198,8 +2328,8 @@ "errorCode": "optional" } ], - "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", - "fieldDoc": "DataSourceSpec embeds the data-source configuration.", + "typeDoc": "DataSourceSpec defines a metric-source type and its implementation-defined connection data.", + "fieldDoc": "DataSourceSpec embeds the complete data-source connection configuration.", "childrenPaths": [ "$.spec.thresholdMetric.dataSourceSpec.description", "$.spec.thresholdMetric.dataSourceSpec.type", @@ -2213,12 +2343,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the data source." + "fieldDoc": "Description optionally summarizes the data source in at most 1,050 characters." }, { "path": "$.spec.thresholdMetric.dataSourceSpec.type", @@ -2232,7 +2366,7 @@ "errorCode": "required" } ], - "fieldDoc": "Type identifies the data-source implementation understood by the consumer." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." }, { "path": "$.spec.thresholdMetric.dataSourceSpec.connectionDetails", @@ -2247,7 +2381,7 @@ "errorCode": "required" } ], - "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON, such as endpoints or authentication settings." }, { "path": "$.spec.thresholdMetric.spec", @@ -2255,7 +2389,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, - "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", + "fieldDoc": "Spec contains implementation-defined query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.thresholdMetric.spec.*~", "$.spec.thresholdMetric.spec.*" @@ -2303,8 +2437,8 @@ ] } ], - "typeDoc": "SLIRatioMetric derives a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 990 good events out of 1,000 total events produce 0.99. 10 bad events with the same total produce the same success ratio.", - "fieldDoc": "RatioMetric supplies a success ratio for an SLO objective.", + "typeDoc": "SLIRatioMetric defines an indicator as [SLIRatioMetric.Good](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Good) divided by [SLIRatioMetric.Total](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Total), ([SLIRatioMetric.Total](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Total) minus [SLIRatioMetric.Bad](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Bad)) divided by [SLIRatioMetric.Total](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Total), or [SLIRatioMetric.Raw](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Raw). [SLIRatioMetric.RawType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.RawType) identifies Raw as a success or failure ratio. For example, 990 good events out of 1,000 total events produce 0.99. 10 bad events with the same total produce the same success ratio.", + "fieldDoc": "RatioMetric defines component queries or a precomputed ratio for an SLO objective.", "childrenPaths": [ "$.spec.ratioMetric.counter", "$.spec.ratioMetric.good", @@ -2332,22 +2466,14 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional", - "conditions": [ - "'total' is set", - "'good' is set" - ] + "errorCode": "optional" }, { "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", - "errorCode": "mutually_exclusive", - "conditions": [ - "'total' is set", - "'good' is set" - ] + "errorCode": "mutually_exclusive" } ], - "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "typeDoc": "SLIMetricSpec supplies an implementation-defined query in the v2alpha flattened layout.", "fieldDoc": "Good is the success-count numerator used with Total.", "childrenPaths": [ "$.spec.ratioMetric.good.dataSourceRef", @@ -2364,28 +2490,16 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional", - "conditions": [ - "'total' is set", - "'good' is set" - ] + "errorCode": "optional" }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length", - "conditions": [ - "'total' is set", - "'good' is set" - ] + "errorCode": "string_dns_label:string_length" }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", - "conditions": [ - "'total' is set", - "'good' is set" - ], "examples": [ "my-name", "123-abc" @@ -2404,15 +2518,11 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional", - "conditions": [ - "'total' is set", - "'good' is set" - ] + "errorCode": "optional" } ], - "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", - "fieldDoc": "DataSourceSpec embeds the data-source configuration.", + "typeDoc": "DataSourceSpec defines a metric-source type and its implementation-defined connection data.", + "fieldDoc": "DataSourceSpec embeds the complete data-source connection configuration.", "childrenPaths": [ "$.spec.ratioMetric.good.dataSourceSpec.description", "$.spec.ratioMetric.good.dataSourceSpec.type", @@ -2426,16 +2536,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", - "errorCode": "string_max_length", - "conditions": [ - "'total' is set", - "'good' is set" - ] + "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the data source." + "fieldDoc": "Description optionally summarizes the data source in at most 1,050 characters." }, { "path": "$.spec.ratioMetric.good.dataSourceSpec.type", @@ -2446,14 +2556,10 @@ "rules": [ { "description": "property is required", - "errorCode": "required", - "conditions": [ - "'total' is set", - "'good' is set" - ] + "errorCode": "required" } ], - "fieldDoc": "Type identifies the data-source implementation understood by the consumer." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." }, { "path": "$.spec.ratioMetric.good.dataSourceSpec.connectionDetails", @@ -2465,14 +2571,10 @@ "rules": [ { "description": "property is required", - "errorCode": "required", - "conditions": [ - "'total' is set", - "'good' is set" - ] + "errorCode": "required" } ], - "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON, such as endpoints or authentication settings." }, { "path": "$.spec.ratioMetric.good.spec", @@ -2480,7 +2582,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, - "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", + "fieldDoc": "Spec contains implementation-defined query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.ratioMetric.good.spec.*~", "$.spec.ratioMetric.good.spec.*" @@ -2510,22 +2612,14 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional", - "conditions": [ - "'total' is set", - "'bad' is set" - ] + "errorCode": "optional" }, { "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", - "errorCode": "mutually_exclusive", - "conditions": [ - "'total' is set", - "'bad' is set" - ] + "errorCode": "mutually_exclusive" } ], - "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "typeDoc": "SLIMetricSpec supplies an implementation-defined query in the v2alpha flattened layout.", "fieldDoc": "Bad is the failure-count input used with Total to derive successes.", "childrenPaths": [ "$.spec.ratioMetric.bad.dataSourceRef", @@ -2542,28 +2636,16 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional", - "conditions": [ - "'total' is set", - "'bad' is set" - ] + "errorCode": "optional" }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length", - "conditions": [ - "'total' is set", - "'bad' is set" - ] + "errorCode": "string_dns_label:string_length" }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", - "conditions": [ - "'total' is set", - "'bad' is set" - ], "examples": [ "my-name", "123-abc" @@ -2582,15 +2664,11 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional", - "conditions": [ - "'total' is set", - "'bad' is set" - ] + "errorCode": "optional" } ], - "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", - "fieldDoc": "DataSourceSpec embeds the data-source configuration.", + "typeDoc": "DataSourceSpec defines a metric-source type and its implementation-defined connection data.", + "fieldDoc": "DataSourceSpec embeds the complete data-source connection configuration.", "childrenPaths": [ "$.spec.ratioMetric.bad.dataSourceSpec.description", "$.spec.ratioMetric.bad.dataSourceSpec.type", @@ -2604,16 +2682,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", - "errorCode": "string_max_length", - "conditions": [ - "'total' is set", - "'bad' is set" - ] + "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the data source." + "fieldDoc": "Description optionally summarizes the data source in at most 1,050 characters." }, { "path": "$.spec.ratioMetric.bad.dataSourceSpec.type", @@ -2624,14 +2702,10 @@ "rules": [ { "description": "property is required", - "errorCode": "required", - "conditions": [ - "'total' is set", - "'bad' is set" - ] + "errorCode": "required" } ], - "fieldDoc": "Type identifies the data-source implementation understood by the consumer." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." }, { "path": "$.spec.ratioMetric.bad.dataSourceSpec.connectionDetails", @@ -2643,14 +2717,10 @@ "rules": [ { "description": "property is required", - "errorCode": "required", - "conditions": [ - "'total' is set", - "'bad' is set" - ] + "errorCode": "required" } ], - "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON, such as endpoints or authentication settings." }, { "path": "$.spec.ratioMetric.bad.spec", @@ -2658,7 +2728,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, - "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", + "fieldDoc": "Spec contains implementation-defined query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.ratioMetric.bad.spec.*~", "$.spec.ratioMetric.bad.spec.*" @@ -2688,20 +2758,14 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional", - "conditions": [ - "'total' is set" - ] + "errorCode": "optional" }, { "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", - "errorCode": "mutually_exclusive", - "conditions": [ - "'total' is set" - ] + "errorCode": "mutually_exclusive" } ], - "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "typeDoc": "SLIMetricSpec supplies an implementation-defined query in the v2alpha flattened layout.", "fieldDoc": "Total is the denominator paired with Good or Bad.", "childrenPaths": [ "$.spec.ratioMetric.total.dataSourceRef", @@ -2718,25 +2782,16 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional", - "conditions": [ - "'total' is set" - ] + "errorCode": "optional" }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length", - "conditions": [ - "'total' is set" - ] + "errorCode": "string_dns_label:string_length" }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", - "conditions": [ - "'total' is set" - ], "examples": [ "my-name", "123-abc" @@ -2755,14 +2810,11 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional", - "conditions": [ - "'total' is set" - ] + "errorCode": "optional" } ], - "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", - "fieldDoc": "DataSourceSpec embeds the data-source configuration.", + "typeDoc": "DataSourceSpec defines a metric-source type and its implementation-defined connection data.", + "fieldDoc": "DataSourceSpec embeds the complete data-source connection configuration.", "childrenPaths": [ "$.spec.ratioMetric.total.dataSourceSpec.description", "$.spec.ratioMetric.total.dataSourceSpec.type", @@ -2776,15 +2828,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", - "errorCode": "string_max_length", - "conditions": [ - "'total' is set" - ] + "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the data source." + "fieldDoc": "Description optionally summarizes the data source in at most 1,050 characters." }, { "path": "$.spec.ratioMetric.total.dataSourceSpec.type", @@ -2795,13 +2848,10 @@ "rules": [ { "description": "property is required", - "errorCode": "required", - "conditions": [ - "'total' is set" - ] + "errorCode": "required" } ], - "fieldDoc": "Type identifies the data-source implementation understood by the consumer." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." }, { "path": "$.spec.ratioMetric.total.dataSourceSpec.connectionDetails", @@ -2813,13 +2863,10 @@ "rules": [ { "description": "property is required", - "errorCode": "required", - "conditions": [ - "'total' is set" - ] + "errorCode": "required" } ], - "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON, such as endpoints or authentication settings." }, { "path": "$.spec.ratioMetric.total.spec", @@ -2827,7 +2874,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, - "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", + "fieldDoc": "Spec contains implementation-defined query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.ratioMetric.total.spec.*~", "$.spec.ratioMetric.total.spec.*" @@ -2887,20 +2934,14 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional", - "conditions": [ - "'raw' is set" - ] + "errorCode": "optional" }, { "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", - "errorCode": "mutually_exclusive", - "conditions": [ - "'raw' is set" - ] + "errorCode": "mutually_exclusive" } ], - "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "typeDoc": "SLIMetricSpec supplies an implementation-defined query in the v2alpha flattened layout.", "fieldDoc": "Raw supplies an already computed ratio.", "childrenPaths": [ "$.spec.ratioMetric.raw.dataSourceRef", @@ -2917,25 +2958,16 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional", - "conditions": [ - "'raw' is set" - ] + "errorCode": "optional" }, { "description": "length must be between 1 and 63", - "errorCode": "string_dns_label:string_length", - "conditions": [ - "'raw' is set" - ] + "errorCode": "string_dns_label:string_length" }, { "description": "string must match regular expression: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'", "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", - "conditions": [ - "'raw' is set" - ], "examples": [ "my-name", "123-abc" @@ -2954,14 +2986,11 @@ "rules": [ { "description": "property is optional", - "errorCode": "optional", - "conditions": [ - "'raw' is set" - ] + "errorCode": "optional" } ], - "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", - "fieldDoc": "DataSourceSpec embeds the data-source configuration.", + "typeDoc": "DataSourceSpec defines a metric-source type and its implementation-defined connection data.", + "fieldDoc": "DataSourceSpec embeds the complete data-source connection configuration.", "childrenPaths": [ "$.spec.ratioMetric.raw.dataSourceSpec.description", "$.spec.ratioMetric.raw.dataSourceSpec.type", @@ -2975,15 +3004,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", - "errorCode": "string_max_length", - "conditions": [ - "'raw' is set" - ] + "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the data source." + "fieldDoc": "Description optionally summarizes the data source in at most 1,050 characters." }, { "path": "$.spec.ratioMetric.raw.dataSourceSpec.type", @@ -2994,13 +3024,10 @@ "rules": [ { "description": "property is required", - "errorCode": "required", - "conditions": [ - "'raw' is set" - ] + "errorCode": "required" } ], - "fieldDoc": "Type identifies the data-source implementation understood by the consumer." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." }, { "path": "$.spec.ratioMetric.raw.dataSourceSpec.connectionDetails", @@ -3012,13 +3039,10 @@ "rules": [ { "description": "property is required", - "errorCode": "required", - "conditions": [ - "'raw' is set" - ] + "errorCode": "required" } ], - "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON, such as endpoints or authentication settings." }, { "path": "$.spec.ratioMetric.raw.spec", @@ -3026,7 +3050,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, - "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", + "fieldDoc": "Spec contains implementation-defined query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.ratioMetric.raw.spec.*~", "$.spec.ratioMetric.raw.spec.*" @@ -3164,8 +3188,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Labels maps label keys to one string value each.", - "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", + "fieldDoc": "Labels optionally classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*" @@ -3219,8 +3249,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps annotation keys to arbitrary string values.", - "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", + "fieldDoc": "Annotations optionally attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -3303,12 +3339,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the SLO." + "fieldDoc": "Description optionally summarizes the SLO in at most 1,050 characters." }, { "path": "$.spec.serviceRef", @@ -3413,8 +3453,17 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set" + ] + } + ], "typeDoc": "Labels maps label keys to one string value each.", - "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", + "fieldDoc": "Labels optionally classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.spec.sli.metadata.labels.*~", "$.spec.sli.metadata.labels.*" @@ -3477,8 +3526,17 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set" + ] + } + ], "typeDoc": "Annotations maps annotation keys to arbitrary string values.", - "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", + "fieldDoc": "Annotations optionally attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.spec.sli.metadata.annotations.*~", "$.spec.sli.metadata.annotations.*" @@ -3537,7 +3595,7 @@ ] } ], - "typeDoc": "SLISpec defines the metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLI).", + "typeDoc": "SLISpec defines the query or queries used to calculate an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLI).", "childrenPaths": [ "$.spec.sli.spec.description", "$.spec.sli.spec.thresholdMetric", @@ -3551,6 +3609,13 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set" + ] + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", @@ -3559,7 +3624,7 @@ ] } ], - "fieldDoc": "Description summarizes the indicator." + "fieldDoc": "Description optionally summarizes the indicator in at most 1,050 characters." }, { "path": "$.spec.sli.spec.thresholdMetric", @@ -3584,8 +3649,8 @@ ] } ], - "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", - "fieldDoc": "ThresholdMetric supplies values for [SLOObjective.Operator](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Operator) to compare with [SLOObjective.Value](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Value).", + "typeDoc": "SLIMetricSpec supplies an implementation-defined query in the v2alpha flattened layout.", + "fieldDoc": "ThresholdMetric defines a query that returns values for comparison with [SLOObjective.Value](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Value) by [SLOObjective.Operator](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Operator).", "childrenPaths": [ "$.spec.sli.spec.thresholdMetric.dataSourceRef", "$.spec.sli.spec.thresholdMetric.dataSourceSpec", @@ -3644,8 +3709,8 @@ ] } ], - "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", - "fieldDoc": "DataSourceSpec embeds the data-source configuration.", + "typeDoc": "DataSourceSpec defines a metric-source type and its implementation-defined connection data.", + "fieldDoc": "DataSourceSpec embeds the complete data-source connection configuration.", "childrenPaths": [ "$.spec.sli.spec.thresholdMetric.dataSourceSpec.description", "$.spec.sli.spec.thresholdMetric.dataSourceSpec.type", @@ -3659,6 +3724,13 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set" + ] + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", @@ -3667,7 +3739,7 @@ ] } ], - "fieldDoc": "Description summarizes the data source." + "fieldDoc": "Description optionally summarizes the data source in at most 1,050 characters." }, { "path": "$.spec.sli.spec.thresholdMetric.dataSourceSpec.type", @@ -3684,7 +3756,7 @@ ] } ], - "fieldDoc": "Type identifies the data-source implementation understood by the consumer." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." }, { "path": "$.spec.sli.spec.thresholdMetric.dataSourceSpec.connectionDetails", @@ -3702,7 +3774,7 @@ ] } ], - "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON, such as endpoints or authentication settings." }, { "path": "$.spec.sli.spec.thresholdMetric.spec", @@ -3710,7 +3782,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, - "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", + "fieldDoc": "Spec contains implementation-defined query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.sli.spec.thresholdMetric.spec.*~", "$.spec.sli.spec.thresholdMetric.spec.*" @@ -3768,8 +3840,8 @@ ] } ], - "typeDoc": "SLIRatioMetric derives a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 990 good events out of 1,000 total events produce 0.99. 10 bad events with the same total produce the same success ratio.", - "fieldDoc": "RatioMetric supplies a success ratio for an SLO objective.", + "typeDoc": "SLIRatioMetric defines an indicator as [SLIRatioMetric.Good](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Good) divided by [SLIRatioMetric.Total](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Total), ([SLIRatioMetric.Total](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Total) minus [SLIRatioMetric.Bad](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Bad)) divided by [SLIRatioMetric.Total](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Total), or [SLIRatioMetric.Raw](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Raw). [SLIRatioMetric.RawType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.RawType) identifies Raw as a success or failure ratio. For example, 990 good events out of 1,000 total events produce 0.99. 10 bad events with the same total produce the same success ratio.", + "fieldDoc": "RatioMetric defines component queries or a precomputed ratio for an SLO objective.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.counter", "$.spec.sli.spec.ratioMetric.good", @@ -3799,22 +3871,18 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ] }, { "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", "errorCode": "mutually_exclusive", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ] } ], - "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "typeDoc": "SLIMetricSpec supplies an implementation-defined query in the v2alpha flattened layout.", "fieldDoc": "Good is the success-count numerator used with Total.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.good.dataSourceRef", @@ -3833,18 +3901,14 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ] }, { "description": "length must be between 1 and 63", "errorCode": "string_dns_label:string_length", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ] }, { @@ -3852,9 +3916,7 @@ "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ], "examples": [ "my-name", @@ -3876,14 +3938,12 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ] } ], - "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", - "fieldDoc": "DataSourceSpec embeds the data-source configuration.", + "typeDoc": "DataSourceSpec defines a metric-source type and its implementation-defined connection data.", + "fieldDoc": "DataSourceSpec embeds the complete data-source connection configuration.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.good.dataSourceSpec.description", "$.spec.sli.spec.ratioMetric.good.dataSourceSpec.type", @@ -3897,17 +3957,22 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set" + ] + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "Description summarizes the data source." + "fieldDoc": "Description optionally summarizes the data source in at most 1,050 characters." }, { "path": "$.spec.sli.spec.ratioMetric.good.dataSourceSpec.type", @@ -3920,13 +3985,11 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "Type identifies the data-source implementation understood by the consumer." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." }, { "path": "$.spec.sli.spec.ratioMetric.good.dataSourceSpec.connectionDetails", @@ -3940,13 +4003,11 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON, such as endpoints or authentication settings." }, { "path": "$.spec.sli.spec.ratioMetric.good.spec", @@ -3954,7 +4015,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, - "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", + "fieldDoc": "Spec contains implementation-defined query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.good.spec.*~", "$.spec.sli.spec.ratioMetric.good.spec.*" @@ -3986,22 +4047,18 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ] }, { "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", "errorCode": "mutually_exclusive", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ] } ], - "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "typeDoc": "SLIMetricSpec supplies an implementation-defined query in the v2alpha flattened layout.", "fieldDoc": "Bad is the failure-count input used with Total to derive successes.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.bad.dataSourceRef", @@ -4020,18 +4077,14 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ] }, { "description": "length must be between 1 and 63", "errorCode": "string_dns_label:string_length", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ] }, { @@ -4039,9 +4092,7 @@ "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ], "examples": [ "my-name", @@ -4063,14 +4114,12 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ] } ], - "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", - "fieldDoc": "DataSourceSpec embeds the data-source configuration.", + "typeDoc": "DataSourceSpec defines a metric-source type and its implementation-defined connection data.", + "fieldDoc": "DataSourceSpec embeds the complete data-source connection configuration.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.bad.dataSourceSpec.description", "$.spec.sli.spec.ratioMetric.bad.dataSourceSpec.type", @@ -4084,17 +4133,22 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set" + ] + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "Description summarizes the data source." + "fieldDoc": "Description optionally summarizes the data source in at most 1,050 characters." }, { "path": "$.spec.sli.spec.ratioMetric.bad.dataSourceSpec.type", @@ -4107,13 +4161,11 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "Type identifies the data-source implementation understood by the consumer." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." }, { "path": "$.spec.sli.spec.ratioMetric.bad.dataSourceSpec.connectionDetails", @@ -4127,13 +4179,11 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON, such as endpoints or authentication settings." }, { "path": "$.spec.sli.spec.ratioMetric.bad.spec", @@ -4141,7 +4191,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, - "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", + "fieldDoc": "Spec contains implementation-defined query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.bad.spec.*~", "$.spec.sli.spec.ratioMetric.bad.spec.*" @@ -4173,20 +4223,18 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ] }, { "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", "errorCode": "mutually_exclusive", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ] } ], - "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "typeDoc": "SLIMetricSpec supplies an implementation-defined query in the v2alpha flattened layout.", "fieldDoc": "Total is the denominator paired with Good or Bad.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.total.dataSourceRef", @@ -4205,16 +4253,14 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ] }, { "description": "length must be between 1 and 63", "errorCode": "string_dns_label:string_length", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ] }, { @@ -4222,8 +4268,7 @@ "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ], "examples": [ "my-name", @@ -4245,13 +4290,12 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ] } ], - "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", - "fieldDoc": "DataSourceSpec embeds the data-source configuration.", + "typeDoc": "DataSourceSpec defines a metric-source type and its implementation-defined connection data.", + "fieldDoc": "DataSourceSpec embeds the complete data-source connection configuration.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.total.dataSourceSpec.description", "$.spec.sli.spec.ratioMetric.total.dataSourceSpec.type", @@ -4265,16 +4309,22 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set" + ] + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "Description summarizes the data source." + "fieldDoc": "Description optionally summarizes the data source in at most 1,050 characters." }, { "path": "$.spec.sli.spec.ratioMetric.total.dataSourceSpec.type", @@ -4287,12 +4337,11 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "Type identifies the data-source implementation understood by the consumer." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." }, { "path": "$.spec.sli.spec.ratioMetric.total.dataSourceSpec.connectionDetails", @@ -4306,12 +4355,11 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON, such as endpoints or authentication settings." }, { "path": "$.spec.sli.spec.ratioMetric.total.spec", @@ -4319,7 +4367,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, - "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", + "fieldDoc": "Spec contains implementation-defined query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.total.spec.*~", "$.spec.sli.spec.ratioMetric.total.spec.*" @@ -4383,20 +4431,18 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ] }, { "description": "exactly one of 'dataSourceRef' and 'dataSourceSpec' must be set", "errorCode": "mutually_exclusive", "conditions": [ - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ] } ], - "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "typeDoc": "SLIMetricSpec supplies an implementation-defined query in the v2alpha flattened layout.", "fieldDoc": "Raw supplies an already computed ratio.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.raw.dataSourceRef", @@ -4415,16 +4461,14 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ] }, { "description": "length must be between 1 and 63", "errorCode": "string_dns_label:string_length", "conditions": [ - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ] }, { @@ -4432,8 +4476,7 @@ "details": "an RFC-1123 compliant label name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character", "errorCode": "string_dns_label:string_match_regexp", "conditions": [ - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ], "examples": [ "my-name", @@ -4455,13 +4498,12 @@ "description": "property is optional", "errorCode": "optional", "conditions": [ - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ] } ], - "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", - "fieldDoc": "DataSourceSpec embeds the data-source configuration.", + "typeDoc": "DataSourceSpec defines a metric-source type and its implementation-defined connection data.", + "fieldDoc": "DataSourceSpec embeds the complete data-source connection configuration.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.raw.dataSourceSpec.description", "$.spec.sli.spec.ratioMetric.raw.dataSourceSpec.type", @@ -4475,16 +4517,22 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "'sli' or 'sliRef' is set" + ] + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", "conditions": [ - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "Description summarizes the data source." + "fieldDoc": "Description optionally summarizes the data source in at most 1,050 characters." }, { "path": "$.spec.sli.spec.ratioMetric.raw.dataSourceSpec.type", @@ -4497,12 +4545,11 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "Type identifies the data-source implementation understood by the consumer." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." }, { "path": "$.spec.sli.spec.ratioMetric.raw.dataSourceSpec.connectionDetails", @@ -4516,12 +4563,11 @@ "description": "property is required", "errorCode": "required", "conditions": [ - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON, such as endpoints or authentication settings." }, { "path": "$.spec.sli.spec.ratioMetric.raw.spec", @@ -4529,7 +4575,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, - "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", + "fieldDoc": "Spec contains implementation-defined query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.sli.spec.ratioMetric.raw.spec.*~", "$.spec.sli.spec.ratioMetric.raw.spec.*" @@ -4623,7 +4669,7 @@ "errorCode": "slice_length" } ], - "fieldDoc": "TimeWindow defines the SLO evaluation period and its alignment." + "fieldDoc": "TimeWindow contains exactly one SLO evaluation window." }, { "path": "$.spec.timeWindow[*]", @@ -4632,7 +4678,12 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "typeDoc": "SLOTimeWindow describes one rolling or calendar-aligned SLO evaluation window.", + "rules": [ + { + "description": "'calendar' must be set when 'isRolling' is false and cannot be set when 'isRolling' is true" + } + ], + "typeDoc": "SLOTimeWindow describes one rolling or calendar-aligned evaluation window. If [SLOTimeWindow.IsRolling](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOTimeWindow.IsRolling) is true, [SLOTimeWindow.Calendar](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOTimeWindow.Calendar) must be nil. If IsRolling is false, Calendar must be non-nil.", "childrenPaths": [ "$.spec.timeWindow[*].duration", "$.spec.timeWindow[*].isRolling", @@ -4724,7 +4775,7 @@ "kind": "[]struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "fieldDoc": "Objectives contains the SLO's budget targets and metric thresholds. V2alpha permits multiple objectives for a threshold-metric SLO." + "fieldDoc": "Objectives contains the SLO's budget targets and metric thresholds. V2alpha permits multiple objectives for a threshold-metric SLO. OpenSLO v1 requires Objectives. The living v2alpha proposal does not state a requiredness change. This SDK accepts an omitted Objectives field." }, { "path": "$.spec.objectives[*]", @@ -4747,7 +4798,7 @@ ] } ], - "typeDoc": "SLOObjective defines one error-budget target and, for a threshold SLI, its metric comparison. The SDK validates threshold-specific fields only for an embedded SLI. A referenced SLI does not expose its metric type during validation.", + "typeDoc": "SLOObjective defines one error-budget target and, for a threshold SLI, its metric comparison. The living v2alpha proposal also defines objective labels, which this SDK does not model.\n\nFor a standard SLO with an inline threshold SLI, validation requires [SLOObjective.Operator](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Operator) and [SLOObjective.Value](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Value). For a standard SLO with an inline ratio SLI, validation forbids them. The SDK does not apply these metric-type rules to referenced SLIs or to SLIs embedded in composite objectives.", "childrenPaths": [ "$.spec.objectives[*].displayName", "$.spec.objectives[*].op", @@ -5027,8 +5078,18 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'sli' or 'sliRef' is set" + ] + } + ], "typeDoc": "Labels maps label keys to one string value each.", - "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", + "fieldDoc": "Labels optionally classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.spec.objectives[*].sli.metadata.labels.*~", "$.spec.objectives[*].sli.metadata.labels.*" @@ -5094,8 +5155,18 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'sli' or 'sliRef' is set" + ] + } + ], "typeDoc": "Annotations maps annotation keys to arbitrary string values.", - "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", + "fieldDoc": "Annotations optionally attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.spec.objectives[*].sli.metadata.annotations.*~", "$.spec.objectives[*].sli.metadata.annotations.*" @@ -5157,7 +5228,7 @@ ] } ], - "typeDoc": "SLISpec defines the metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLI).", + "typeDoc": "SLISpec defines the query or queries used to calculate an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLI).", "childrenPaths": [ "$.spec.objectives[*].sli.spec.description", "$.spec.objectives[*].sli.spec.thresholdMetric", @@ -5171,6 +5242,14 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'sli' or 'sliRef' is set" + ] + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", @@ -5180,7 +5259,7 @@ ] } ], - "fieldDoc": "Description summarizes the indicator." + "fieldDoc": "Description optionally summarizes the indicator in at most 1,050 characters." }, { "path": "$.spec.objectives[*].sli.spec.thresholdMetric", @@ -5207,8 +5286,8 @@ ] } ], - "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", - "fieldDoc": "ThresholdMetric supplies values for [SLOObjective.Operator](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Operator) to compare with [SLOObjective.Value](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Value).", + "typeDoc": "SLIMetricSpec supplies an implementation-defined query in the v2alpha flattened layout.", + "fieldDoc": "ThresholdMetric defines a query that returns values for comparison with [SLOObjective.Value](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Value) by [SLOObjective.Operator](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOObjective.Operator).", "childrenPaths": [ "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceRef", "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceSpec", @@ -5271,8 +5350,8 @@ ] } ], - "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", - "fieldDoc": "DataSourceSpec embeds the data-source configuration.", + "typeDoc": "DataSourceSpec defines a metric-source type and its implementation-defined connection data.", + "fieldDoc": "DataSourceSpec embeds the complete data-source connection configuration.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceSpec.description", "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceSpec.type", @@ -5286,6 +5365,14 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'sli' or 'sliRef' is set" + ] + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", @@ -5295,7 +5382,7 @@ ] } ], - "fieldDoc": "Description summarizes the data source." + "fieldDoc": "Description optionally summarizes the data source in at most 1,050 characters." }, { "path": "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceSpec.type", @@ -5313,7 +5400,7 @@ ] } ], - "fieldDoc": "Type identifies the data-source implementation understood by the consumer." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." }, { "path": "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceSpec.connectionDetails", @@ -5332,7 +5419,7 @@ ] } ], - "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON, such as endpoints or authentication settings." }, { "path": "$.spec.objectives[*].sli.spec.thresholdMetric.spec", @@ -5340,7 +5427,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, - "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", + "fieldDoc": "Spec contains implementation-defined query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.thresholdMetric.spec.*~", "$.spec.objectives[*].sli.spec.thresholdMetric.spec.*" @@ -5402,8 +5489,8 @@ ] } ], - "typeDoc": "SLIRatioMetric derives a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 990 good events out of 1,000 total events produce 0.99. 10 bad events with the same total produce the same success ratio.", - "fieldDoc": "RatioMetric supplies a success ratio for an SLO objective.", + "typeDoc": "SLIRatioMetric defines an indicator as [SLIRatioMetric.Good](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Good) divided by [SLIRatioMetric.Total](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Total), ([SLIRatioMetric.Total](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Total) minus [SLIRatioMetric.Bad](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Bad)) divided by [SLIRatioMetric.Total](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Total), or [SLIRatioMetric.Raw](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.Raw). [SLIRatioMetric.RawType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIRatioMetric.RawType) identifies Raw as a success or failure ratio. For example, 990 good events out of 1,000 total events produce 0.99. 10 bad events with the same total produce the same success ratio.", + "fieldDoc": "RatioMetric defines component queries or a precomputed ratio for an SLO objective.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.counter", "$.spec.objectives[*].sli.spec.ratioMetric.good", @@ -5434,9 +5521,7 @@ "errorCode": "optional", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ] }, { @@ -5444,13 +5529,11 @@ "errorCode": "mutually_exclusive", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ] } ], - "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "typeDoc": "SLIMetricSpec supplies an implementation-defined query in the v2alpha flattened layout.", "fieldDoc": "Good is the success-count numerator used with Total.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceRef", @@ -5470,9 +5553,7 @@ "errorCode": "optional", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ] }, { @@ -5480,9 +5561,7 @@ "errorCode": "string_dns_label:string_length", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ] }, { @@ -5491,9 +5570,7 @@ "errorCode": "string_dns_label:string_match_regexp", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ], "examples": [ "my-name", @@ -5516,14 +5593,12 @@ "errorCode": "optional", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ] } ], - "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", - "fieldDoc": "DataSourceSpec embeds the data-source configuration.", + "typeDoc": "DataSourceSpec defines a metric-source type and its implementation-defined connection data.", + "fieldDoc": "DataSourceSpec embeds the complete data-source connection configuration.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceSpec.description", "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceSpec.type", @@ -5537,18 +5612,24 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'sli' or 'sliRef' is set" + ] + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "Description summarizes the data source." + "fieldDoc": "Description optionally summarizes the data source in at most 1,050 characters." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceSpec.type", @@ -5562,13 +5643,11 @@ "errorCode": "required", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "Type identifies the data-source implementation understood by the consumer." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceSpec.connectionDetails", @@ -5583,13 +5662,11 @@ "errorCode": "required", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'good' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON, such as endpoints or authentication settings." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.good.spec", @@ -5597,7 +5674,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, - "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", + "fieldDoc": "Spec contains implementation-defined query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.good.spec.*~", "$.spec.objectives[*].sli.spec.ratioMetric.good.spec.*" @@ -5630,9 +5707,7 @@ "errorCode": "optional", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ] }, { @@ -5640,13 +5715,11 @@ "errorCode": "mutually_exclusive", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ] } ], - "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "typeDoc": "SLIMetricSpec supplies an implementation-defined query in the v2alpha flattened layout.", "fieldDoc": "Bad is the failure-count input used with Total to derive successes.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceRef", @@ -5666,9 +5739,7 @@ "errorCode": "optional", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ] }, { @@ -5676,9 +5747,7 @@ "errorCode": "string_dns_label:string_length", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ] }, { @@ -5687,9 +5756,7 @@ "errorCode": "string_dns_label:string_match_regexp", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ], "examples": [ "my-name", @@ -5712,14 +5779,12 @@ "errorCode": "optional", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ] } ], - "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", - "fieldDoc": "DataSourceSpec embeds the data-source configuration.", + "typeDoc": "DataSourceSpec defines a metric-source type and its implementation-defined connection data.", + "fieldDoc": "DataSourceSpec embeds the complete data-source connection configuration.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceSpec.description", "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceSpec.type", @@ -5733,18 +5798,24 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'sli' or 'sliRef' is set" + ] + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "Description summarizes the data source." + "fieldDoc": "Description optionally summarizes the data source in at most 1,050 characters." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceSpec.type", @@ -5758,13 +5829,11 @@ "errorCode": "required", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "Type identifies the data-source implementation understood by the consumer." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceSpec.connectionDetails", @@ -5779,13 +5848,11 @@ "errorCode": "required", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set", - "'bad' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON, such as endpoints or authentication settings." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.bad.spec", @@ -5793,7 +5860,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, - "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", + "fieldDoc": "Spec contains implementation-defined query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.bad.spec.*~", "$.spec.objectives[*].sli.spec.ratioMetric.bad.spec.*" @@ -5826,8 +5893,7 @@ "errorCode": "optional", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ] }, { @@ -5835,12 +5901,11 @@ "errorCode": "mutually_exclusive", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ] } ], - "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "typeDoc": "SLIMetricSpec supplies an implementation-defined query in the v2alpha flattened layout.", "fieldDoc": "Total is the denominator paired with Good or Bad.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceRef", @@ -5860,8 +5925,7 @@ "errorCode": "optional", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ] }, { @@ -5869,8 +5933,7 @@ "errorCode": "string_dns_label:string_length", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ] }, { @@ -5879,8 +5942,7 @@ "errorCode": "string_dns_label:string_match_regexp", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ], "examples": [ "my-name", @@ -5903,13 +5965,12 @@ "errorCode": "optional", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ] } ], - "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", - "fieldDoc": "DataSourceSpec embeds the data-source configuration.", + "typeDoc": "DataSourceSpec defines a metric-source type and its implementation-defined connection data.", + "fieldDoc": "DataSourceSpec embeds the complete data-source connection configuration.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceSpec.description", "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceSpec.type", @@ -5923,17 +5984,24 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'sli' or 'sliRef' is set" + ] + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "Description summarizes the data source." + "fieldDoc": "Description optionally summarizes the data source in at most 1,050 characters." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceSpec.type", @@ -5947,12 +6015,11 @@ "errorCode": "required", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "Type identifies the data-source implementation understood by the consumer." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceSpec.connectionDetails", @@ -5967,12 +6034,11 @@ "errorCode": "required", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'total' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON, such as endpoints or authentication settings." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.total.spec", @@ -5980,7 +6046,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, - "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", + "fieldDoc": "Spec contains implementation-defined query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.total.spec.*~", "$.spec.objectives[*].sli.spec.ratioMetric.total.spec.*" @@ -6047,8 +6113,7 @@ "errorCode": "optional", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ] }, { @@ -6056,12 +6121,11 @@ "errorCode": "mutually_exclusive", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ] } ], - "typeDoc": "SLIMetricSpec supplies a provider-specific query in the v2alpha flattened layout.", + "typeDoc": "SLIMetricSpec supplies an implementation-defined query in the v2alpha flattened layout.", "fieldDoc": "Raw supplies an already computed ratio.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceRef", @@ -6081,8 +6145,7 @@ "errorCode": "optional", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ] }, { @@ -6090,8 +6153,7 @@ "errorCode": "string_dns_label:string_length", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ] }, { @@ -6100,8 +6162,7 @@ "errorCode": "string_dns_label:string_match_regexp", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ], "examples": [ "my-name", @@ -6124,13 +6185,12 @@ "errorCode": "optional", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ] } ], - "typeDoc": "DataSourceSpec defines a data-source type and its provider-specific connection configuration.", - "fieldDoc": "DataSourceSpec embeds the data-source configuration.", + "typeDoc": "DataSourceSpec defines a metric-source type and its implementation-defined connection data.", + "fieldDoc": "DataSourceSpec embeds the complete data-source connection configuration.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceSpec.description", "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceSpec.type", @@ -6144,17 +6204,24 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'sli' or 'sliRef' is set" + ] + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "Description summarizes the data source." + "fieldDoc": "Description optionally summarizes the data source in at most 1,050 characters." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceSpec.type", @@ -6168,12 +6235,11 @@ "errorCode": "required", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "Type identifies the data-source implementation understood by the consumer." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceSpec.connectionDetails", @@ -6188,12 +6254,11 @@ "errorCode": "required", "conditions": [ "is composite SLO", - "'sli' or 'sliRef' is set", - "'raw' is set" + "'sli' or 'sliRef' is set" ] } ], - "fieldDoc": "ConnectionDetails stores provider-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON, such as endpoints or authentication settings." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.raw.spec", @@ -6201,7 +6266,7 @@ "name": "map[string]interface {}", "kind": "map[string]interface" }, - "fieldDoc": "Spec contains provider-specific query configuration at the same level as the data-source selection.", + "fieldDoc": "Spec contains implementation-defined query configuration at the same level as the data-source selection.", "childrenPaths": [ "$.spec.objectives[*].sli.spec.ratioMetric.raw.spec.*~", "$.spec.objectives[*].sli.spec.ratioMetric.raw.spec.*" @@ -6282,7 +6347,7 @@ ] } ], - "fieldDoc": "CompositeWeight scales this objective's contribution to a multi-objective composite SLO. OpenSLO uses a weight of 1 when omitted. This SDK leaves the field unset." + "fieldDoc": "CompositeWeight scales this objective's contribution to a composite SLO. The living v2alpha proposal permits it only with multiple objectives and defaults it to 1. This SDK does not enforce the objective-count restriction and preserves an omitted value as nil." }, { "path": "$.spec.alertPolicies", @@ -6291,7 +6356,7 @@ "kind": "[]struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "fieldDoc": "AlertPolicies contains inline or referenced policies associated with the SLO." + "fieldDoc": "AlertPolicies contains policies associated with the SLO. Each item must specify exactly one inline definition or metadata-name reference." }, { "path": "$.spec.alertPolicies[*]", @@ -6310,7 +6375,7 @@ "errorCode": "optional" } ], - "typeDoc": "SLOAlertPolicy associates an alert policy with an [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLO).", + "typeDoc": "SLOAlertPolicy associates exactly one inline or referenced alert policy with an [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLO).", "childrenPaths": [ "$.spec.alertPolicies[*].kind", "$.spec.alertPolicies[*].metadata", @@ -6394,8 +6459,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Labels maps label keys to one string value each.", - "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", + "fieldDoc": "Labels optionally classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.spec.alertPolicies[*].metadata.labels.*~", "$.spec.alertPolicies[*].metadata.labels.*" @@ -6449,8 +6520,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps annotation keys to arbitrary string values.", - "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", + "fieldDoc": "Annotations optionally attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.spec.alertPolicies[*].metadata.annotations.*~", "$.spec.alertPolicies[*].metadata.annotations.*" @@ -6500,7 +6577,7 @@ "errorCode": "required" } ], - "typeDoc": "AlertPolicySpec defines the trigger states, condition, and notification destinations for an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy).", + "typeDoc": "AlertPolicySpec defines the trigger states, condition, and notification destinations for an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy). The trigger flags are independent and have a false zero value. JSON encoding omits false values. This SDK applies no omission default and accepts all three flags as false.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.description", "$.spec.alertPolicies[*].spec.alertWhenNoData", @@ -6519,12 +6596,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the alert policy." + "fieldDoc": "Description optionally summarizes the alert policy in at most 1,050 characters." }, { "path": "$.spec.alertPolicies[*].spec.alertWhenNoData", @@ -6532,7 +6613,13 @@ "name": "bool", "kind": "bool" }, - "fieldDoc": "AlertWhenNoData enables notifications when the associated SLO has no burn-rate value." + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "fieldDoc": "AlertWhenNoData controls whether a missing burn-rate value triggers an alert." }, { "path": "$.spec.alertPolicies[*].spec.alertWhenBreaching", @@ -6540,7 +6627,13 @@ "name": "bool", "kind": "bool" }, - "fieldDoc": "AlertWhenBreaching enables notifications when the condition is breaching." + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "fieldDoc": "AlertWhenBreaching controls whether a breaching condition triggers an alert." }, { "path": "$.spec.alertPolicies[*].spec.alertWhenResolved", @@ -6548,7 +6641,13 @@ "name": "bool", "kind": "bool" }, - "fieldDoc": "AlertWhenResolved enables notifications when the condition resolves." + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "fieldDoc": "AlertWhenResolved controls whether a resolved condition triggers an alert." }, { "path": "$.spec.alertPolicies[*].spec.conditions", @@ -6563,7 +6662,7 @@ "errorCode": "slice_length" } ], - "fieldDoc": "Conditions supplies the policy's alert condition by reference or inline." + "fieldDoc": "Conditions contains exactly one alert condition, specified inline or by reference." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*]", @@ -6582,7 +6681,7 @@ "errorCode": "optional" } ], - "typeDoc": "AlertPolicyCondition supplies a condition to an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy) by reference or inline definition.", + "typeDoc": "AlertPolicyCondition supplies exactly one condition to an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy). Set either the reference or the inline definition.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].conditionRef", "$.spec.alertPolicies[*].spec.conditions[*].kind", @@ -6615,7 +6714,7 @@ ] } ], - "fieldDoc": "ConditionRef names the alert condition to use." + "fieldDoc": "ConditionRef is the metadata name of the alert condition to use." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].kind", @@ -6693,8 +6792,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Labels maps label keys to one string value each.", - "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", + "fieldDoc": "Labels optionally classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].metadata.labels.*~", "$.spec.alertPolicies[*].spec.conditions[*].metadata.labels.*" @@ -6748,8 +6853,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps annotation keys to arbitrary string values.", - "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", + "fieldDoc": "Annotations optionally attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].metadata.annotations.*~", "$.spec.alertPolicies[*].spec.conditions[*].metadata.annotations.*" @@ -6863,7 +6974,7 @@ } ], "typeDoc": "AlertConditionKind identifies the evaluation algorithm for an [AlertConditionType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionType).", - "fieldDoc": "Kind selects the condition algorithm. OpenSLO defaults omitted values to [AlertConditionKindBurnRate](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionKindBurnRate). This SDK does not apply that default." + "fieldDoc": "Kind selects the condition algorithm. This SDK requires Kind and does not apply the OpenSLO v1 default of [AlertConditionKindBurnRate](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionKindBurnRate)." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.condition.op", @@ -6950,7 +7061,7 @@ } ], "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", - "fieldDoc": "AlertAfter sets how long the condition must remain valid before an alert is triggered. OpenSLO defaults omitted values to \"0m\". This SDK does not apply that default." + "fieldDoc": "AlertAfter sets how long the burn-rate comparison must remain true before the condition becomes breaching. This SDK requires AlertAfter and does not apply the OpenSLO v1 default of \"0m\"." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.description", @@ -6959,12 +7070,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the alert condition." + "fieldDoc": "Description optionally summarizes the alert condition in at most 1,050 characters." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets", @@ -6979,7 +7094,7 @@ "errorCode": "slice_min_length" } ], - "fieldDoc": "NotificationTargets lists referenced or inline delivery destinations." + "fieldDoc": "NotificationTargets contains one or more delivery destinations. Specify each destination inline or by reference." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets[*]", @@ -6998,7 +7113,7 @@ "errorCode": "optional" } ], - "typeDoc": "AlertPolicyNotificationTarget supplies a notification destination to an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy) by reference or inline definition.", + "typeDoc": "AlertPolicyNotificationTarget supplies exactly one notification destination to an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicy). Set either the reference or the inline definition.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].targetRef", "$.spec.alertPolicies[*].spec.notificationTargets[*].kind", @@ -7031,7 +7146,7 @@ ] } ], - "fieldDoc": "TargetRef names the notification target to use." + "fieldDoc": "TargetRef is the metadata name of the notification target to use." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets[*].kind", @@ -7109,8 +7224,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Labels maps label keys to one string value each.", - "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", + "fieldDoc": "Labels optionally classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.labels.*~", "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.labels.*" @@ -7164,8 +7285,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps annotation keys to arbitrary string values.", - "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", + "fieldDoc": "Annotations optionally attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.annotations.*~", "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.annotations.*" @@ -7215,7 +7342,7 @@ "errorCode": "required" } ], - "typeDoc": "AlertNotificationTargetSpec identifies the consumer-defined notification destination.", + "typeDoc": "AlertNotificationTargetSpec identifies a notification destination. The consuming implementation defines the required [AlertNotificationTargetSpec.Target](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertNotificationTargetSpec.Target) format.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].spec.description", "$.spec.alertPolicies[*].spec.notificationTargets[*].spec.target" @@ -7228,12 +7355,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the destination." + "fieldDoc": "Description optionally summarizes the target in at most 1,050 characters." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets[*].spec.target", @@ -7247,7 +7378,7 @@ "errorCode": "required" } ], - "fieldDoc": "Target identifies the consumer-defined destination for alert delivery." + "fieldDoc": "Target specifies the notification destination in the format that the consuming implementation requires. Examples include \"email\", \"slack\", \"web-hook\", and \"Opsgenie\"." }, { "path": "$.spec.alertPolicies[*].alertPolicyRef", @@ -7274,7 +7405,7 @@ ] } ], - "fieldDoc": "AlertPolicyRef names the alert policy to use." + "fieldDoc": "AlertPolicyRef is the metadata name of the alert policy to use." } ] }, @@ -7288,7 +7419,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "typeDoc": "Service groups SLOs under a named service.", + "typeDoc": "Service identifies a high-level group for SLOs. Each [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLO) refers to a Service by metadata name through [SLOSpec.ServiceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOSpec.ServiceRef). Multiple SLOs can use the same Service name. The SDK does not verify that a referenced Service exists.\n\nThis type is the SDK's v2alpha representation. The living, unstable [OpenSLO v2alpha proposal](https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md) does not define a standalone Service schema.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -7394,8 +7525,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Labels maps label keys to one string value each.", - "fieldDoc": "Labels classifies the object with Kubernetes-style, single-valued labels.", + "fieldDoc": "Labels optionally classifies the object with Kubernetes-style, single-valued labels.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*" @@ -7449,8 +7586,14 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps annotation keys to arbitrary string values.", - "fieldDoc": "Annotations attaches non-identifying metadata with qualified keys.", + "fieldDoc": "Annotations optionally attaches non-identifying metadata with qualified keys.", "childrenPaths": [ "$.metadata.annotations.*~", "$.metadata.annotations.*" @@ -7506,12 +7649,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the service." + "fieldDoc": "Description optionally summarizes the service in at most 1,050 characters." } ] } @@ -7527,7 +7674,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "AlertCondition defines the condition under which an SLO alert is considered breaching.", + "typeDoc": "AlertCondition defines a burn-rate condition for an SLO. An [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicy) controls whether a breaching condition triggers an alert.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -7652,8 +7799,14 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "Labels maps metadata keys to one or more values.", - "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "typeDoc": "Labels maps each metadata key to zero or more values. JSON decoding accepts each map value as a string or an array of strings.", + "fieldDoc": "Labels contains optional metadata associated with the object.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*", @@ -7695,6 +7848,12 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ @@ -7778,6 +7937,7 @@ } ], "typeDoc": "AlertConditionType defines a comparison against an SLO's burn rate. Burn rate is error-budget consumption relative to the rate allowed by the SLO.", + "fieldDoc": "Condition defines the burn-rate comparison used to determine whether this alert condition is breaching.", "childrenPaths": [ "$.spec.condition.kind", "$.spec.condition.op", @@ -7894,7 +8054,7 @@ } ], "typeDoc": "DurationShorthand represents a duration as an integer and a case-sensitive [DurationShorthandUnit](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DurationShorthandUnit), such as \"1m\" or \"10d\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", - "fieldDoc": "AlertAfter sets how long the condition must remain valid before an alert is triggered. OpenSLO treats an omitted value as \"0m\". This SDK leaves it unset." + "fieldDoc": "AlertAfter sets how long the burn-rate comparison must remain true before the condition becomes breaching. An [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicy) controls whether that state triggers an alert. OpenSLO treats an omitted value as \"0m\". This SDK leaves it unset." }, { "path": "$.spec.description", @@ -7903,6 +8063,10 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" @@ -8047,8 +8211,14 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "Labels maps metadata keys to one or more values.", - "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "typeDoc": "Labels maps each metadata key to zero or more values. JSON decoding accepts each map value as a string or an array of strings.", + "fieldDoc": "Labels contains optional metadata associated with the object.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*", @@ -8090,6 +8260,12 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ @@ -8145,12 +8321,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the destination." + "fieldDoc": "Description optionally describes the notification target." }, { "path": "$.spec.target", @@ -8164,7 +8344,7 @@ "errorCode": "required" } ], - "fieldDoc": "Target identifies the implementation-defined delivery target." + "fieldDoc": "Target specifies the notification destination in the format required by the consuming implementation. Examples include email, Slack, a webhook, and Opsgenie." } ] }, @@ -8178,7 +8358,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "AlertPolicy defines when the system emits alerts for an SLO and where it sends them. It is distinct from an organization's policy for responding to error-budget consumption.", + "typeDoc": "AlertPolicy defines which alert-condition states trigger an SLO alert. It also defines where the consuming system delivers the resulting notifications. It is distinct from an organization's policy for responding to error-budget consumption.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -8303,8 +8483,14 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "Labels maps metadata keys to one or more values.", - "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "typeDoc": "Labels maps each metadata key to zero or more values. JSON decoding accepts each map value as a string or an array of strings.", + "fieldDoc": "Labels contains optional metadata associated with the object.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*", @@ -8346,6 +8532,12 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ @@ -8388,7 +8580,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "AlertPolicySpec defines the events that emit notifications and the condition and destinations used by an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicy).", + "typeDoc": "AlertPolicySpec defines which condition states trigger an SLO alert and where the consuming system delivers the resulting notifications. The trigger fields have a false zero value. JSON encoding omits false values, and this SDK accepts all three fields as false.", "childrenPaths": [ "$.spec.description", "$.spec.alertWhenNoData", @@ -8407,6 +8599,10 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" @@ -8420,7 +8616,13 @@ "name": "bool", "kind": "bool" }, - "fieldDoc": "AlertWhenNoData enables alerts when the associated SLO has no burn-rate value." + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "fieldDoc": "AlertWhenNoData reports whether to trigger an alert when the associated [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO) has no burn-rate value. Its zero value is false." }, { "path": "$.spec.alertWhenBreaching", @@ -8428,7 +8630,13 @@ "name": "bool", "kind": "bool" }, - "fieldDoc": "AlertWhenBreaching enables alerts when the condition is breaching." + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "fieldDoc": "AlertWhenBreaching reports whether to trigger an alert when the condition is breaching. Its zero value is false." }, { "path": "$.spec.alertWhenResolved", @@ -8436,7 +8644,13 @@ "name": "bool", "kind": "bool" }, - "fieldDoc": "AlertWhenResolved enables alerts when the condition resolves." + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "fieldDoc": "AlertWhenResolved reports whether to trigger an alert when the condition resolves. Its zero value is false." }, { "path": "$.spec.conditions", @@ -8451,7 +8665,7 @@ "errorCode": "slice_length" } ], - "fieldDoc": "Conditions contains the alert condition, inline or by reference." + "fieldDoc": "Conditions contains exactly one alert condition, specified inline or by reference." }, { "path": "$.spec.conditions[*]", @@ -8470,7 +8684,7 @@ "errorCode": "optional" } ], - "typeDoc": "AlertPolicyCondition supplies an alert condition to an [AlertPolicySpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicySpec) by reference or inline definition.", + "typeDoc": "AlertPolicyCondition supplies exactly one alert condition representation to an [AlertPolicySpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicySpec). Set [AlertPolicyConditionInline](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicyConditionInline) or [AlertPolicyConditionRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicyConditionRef), but not both.", "childrenPaths": [ "$.spec.conditions[*].conditionRef", "$.spec.conditions[*].kind", @@ -8503,7 +8717,7 @@ ] } ], - "fieldDoc": "ConditionRef names an existing alert condition." + "fieldDoc": "ConditionRef matches the [Metadata.Name](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#Metadata.Name) of an existing [AlertCondition](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertCondition)." }, { "path": "$.spec.conditions[*].kind", @@ -8600,8 +8814,14 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "Labels maps metadata keys to one or more values.", - "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "typeDoc": "Labels maps each metadata key to zero or more values. JSON decoding accepts each map value as a string or an array of strings.", + "fieldDoc": "Labels contains optional metadata associated with the object.", "childrenPaths": [ "$.spec.conditions[*].metadata.labels.*~", "$.spec.conditions[*].metadata.labels.*", @@ -8643,6 +8863,12 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ @@ -8726,6 +8952,7 @@ } ], "typeDoc": "AlertConditionType defines a comparison against an SLO's burn rate. Burn rate is error-budget consumption relative to the rate allowed by the SLO.", + "fieldDoc": "Condition defines the burn-rate comparison used to determine whether this alert condition is breaching.", "childrenPaths": [ "$.spec.conditions[*].spec.condition.kind", "$.spec.conditions[*].spec.condition.op", @@ -8842,7 +9069,7 @@ } ], "typeDoc": "DurationShorthand represents a duration as an integer and a case-sensitive [DurationShorthandUnit](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DurationShorthandUnit), such as \"1m\" or \"10d\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", - "fieldDoc": "AlertAfter sets how long the condition must remain valid before an alert is triggered. OpenSLO treats an omitted value as \"0m\". This SDK leaves it unset." + "fieldDoc": "AlertAfter sets how long the burn-rate comparison must remain true before the condition becomes breaching. An [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicy) controls whether that state triggers an alert. OpenSLO treats an omitted value as \"0m\". This SDK leaves it unset." }, { "path": "$.spec.conditions[*].spec.description", @@ -8851,6 +9078,10 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" @@ -8871,7 +9102,7 @@ "errorCode": "slice_min_length" } ], - "fieldDoc": "NotificationTargets lists notification destinations, inline or by reference." + "fieldDoc": "NotificationTargets contains one or more notification destinations. Specify each destination inline or by reference." }, { "path": "$.spec.notificationTargets[*]", @@ -8890,7 +9121,7 @@ "errorCode": "optional" } ], - "typeDoc": "AlertPolicyNotificationTarget supplies a notification target to an [AlertPolicySpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicySpec) by reference or inline definition.", + "typeDoc": "AlertPolicyNotificationTarget supplies exactly one notification-target representation to an [AlertPolicySpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicySpec). Set [AlertPolicyNotificationTargetInline](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicyNotificationTargetInline) or [AlertPolicyNotificationTargetRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicyNotificationTargetRef), but not both.", "childrenPaths": [ "$.spec.notificationTargets[*].targetRef", "$.spec.notificationTargets[*].kind", @@ -8923,7 +9154,7 @@ ] } ], - "fieldDoc": "TargetRef names an existing notification target." + "fieldDoc": "TargetRef matches the [Metadata.Name](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#Metadata.Name) of an existing [AlertNotificationTarget](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertNotificationTarget)." }, { "path": "$.spec.notificationTargets[*].kind", @@ -9020,8 +9251,14 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "Labels maps metadata keys to one or more values.", - "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "typeDoc": "Labels maps each metadata key to zero or more values. JSON decoding accepts each map value as a string or an array of strings.", + "fieldDoc": "Labels contains optional metadata associated with the object.", "childrenPaths": [ "$.spec.notificationTargets[*].metadata.labels.*~", "$.spec.notificationTargets[*].metadata.labels.*", @@ -9063,6 +9300,12 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ @@ -9124,12 +9367,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the destination." + "fieldDoc": "Description optionally describes the notification target." }, { "path": "$.spec.notificationTargets[*].spec.target", @@ -9143,7 +9390,7 @@ "errorCode": "required" } ], - "fieldDoc": "Target identifies the implementation-defined delivery target." + "fieldDoc": "Target specifies the notification destination in the format required by the consuming implementation. Examples include email, Slack, a webhook, and Opsgenie." } ] }, @@ -9157,7 +9404,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "DataSource stores reusable connection details for a metrics backend.", + "typeDoc": "DataSource represents reusable connection details for a metric source. [SLIMetricSource.MetricSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.MetricSourceRef) selects it by [Metadata.Name](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#Metadata.Name). A referenced metric source can omit [SLIMetricSource.Type](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.Type) and keep connection details, such as authentication settings, outside the [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI). An SLI can instead define an inline metric source with [SLIMetricSource.Type](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.Type) and [SLIMetricSource.Spec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.Spec).", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -9282,8 +9529,14 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "Labels maps metadata keys to one or more values.", - "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "typeDoc": "Labels maps each metadata key to zero or more values. JSON decoding accepts each map value as a string or an array of strings.", + "fieldDoc": "Labels contains optional metadata associated with the object.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*", @@ -9325,6 +9578,12 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ @@ -9381,6 +9640,10 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" @@ -9400,7 +9663,7 @@ "errorCode": "required" } ], - "fieldDoc": "Type names the implementation-defined metric source." + "fieldDoc": "Type identifies the implementation-defined metric source type, such as Prometheus or Datadog." }, { "path": "$.spec.connectionDetails", @@ -9415,7 +9678,7 @@ "errorCode": "required" } ], - "fieldDoc": "ConnectionDetails stores source-specific connection configuration as JSON." + "fieldDoc": "ConnectionDetails contains implementation-defined connection data encoded as JSON. The metric-source implementation defines its fields, which can include endpoints or authentication settings." } ] }, @@ -9429,7 +9692,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "SLI describes how to read metric data used to evaluate an [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO).", + "typeDoc": "SLI defines a derived reliability indicator and the queries used to calculate it for an [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO).", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -9554,8 +9817,14 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "Labels maps metadata keys to one or more values.", - "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "typeDoc": "Labels maps each metadata key to zero or more values. JSON decoding accepts each map value as a string or an array of strings.", + "fieldDoc": "Labels contains optional metadata associated with the object.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*", @@ -9597,6 +9866,12 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ @@ -9645,7 +9920,7 @@ "errorCode": "mutually_exclusive" } ], - "typeDoc": "SLISpec defines the metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "typeDoc": "SLISpec defines the query or queries used to calculate an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", "childrenPaths": [ "$.spec.description", "$.spec.thresholdMetric", @@ -9659,6 +9934,10 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" @@ -9679,8 +9958,8 @@ "errorCode": "optional" } ], - "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", - "fieldDoc": "ThresholdMetric retrieves raw values for an [SLOObjective](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective) to compare with its Operator and Value.", + "typeDoc": "SLIMetricSpec defines one query used to read metric data for an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "ThresholdMetric defines a query that returns raw values. [SLOObjective.Operator](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective.Operator) compares each value with [SLOObjective.Value](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective.Value).", "childrenPaths": [ "$.spec.thresholdMetric.metricSource" ] @@ -9738,7 +10017,7 @@ "name": "string", "kind": "string" }, - "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires Type when [SLIMetricSource.MetricSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.MetricSourceRef) is omitted. Otherwise, OpenSLO infers Type from the referenced [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource). This SDK does not validate the requirement." }, { "path": "$.spec.thresholdMetric.metricSource.spec", @@ -9804,7 +10083,7 @@ ] } ], - "typeDoc": "SLIRatioMetric defines a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 99 good events out of 100 produce a ratio of 0.99. One bad event out of 100 produces the same ratio.", + "typeDoc": "SLIRatioMetric defines an indicator from good divided by total or (total minus bad) divided by total. It can instead use a precomputed success or failure ratio identified by [SLIRatioMetric.RawType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIRatioMetric.RawType). For example, 99 good events out of 100 produce a ratio of 0.99. One bad event out of 100 produces the same ratio.", "childrenPaths": [ "$.spec.ratioMetric.counter", "$.spec.ratioMetric.good", @@ -9839,7 +10118,7 @@ ] } ], - "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "typeDoc": "SLIMetricSpec defines one query used to read metric data for an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", "fieldDoc": "Good supplies the numerator for a good-over-total ratio.", "childrenPaths": [ "$.spec.ratioMetric.good.metricSource" @@ -9914,7 +10193,7 @@ "name": "string", "kind": "string" }, - "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires Type when [SLIMetricSource.MetricSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.MetricSourceRef) is omitted. Otherwise, OpenSLO infers Type from the referenced [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource). This SDK does not validate the requirement." }, { "path": "$.spec.ratioMetric.good.metricSource.spec", @@ -9977,7 +10256,7 @@ ] } ], - "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "typeDoc": "SLIMetricSpec defines one query used to read metric data for an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", "fieldDoc": "Bad supplies the number subtracted from Total for a failure-based ratio.", "childrenPaths": [ "$.spec.ratioMetric.bad.metricSource" @@ -10052,7 +10331,7 @@ "name": "string", "kind": "string" }, - "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires Type when [SLIMetricSource.MetricSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.MetricSourceRef) is omitted. Otherwise, OpenSLO infers Type from the referenced [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource). This SDK does not validate the requirement." }, { "path": "$.spec.ratioMetric.bad.metricSource.spec", @@ -10114,7 +10393,7 @@ ] } ], - "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "typeDoc": "SLIMetricSpec defines one query used to read metric data for an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", "fieldDoc": "Total supplies the denominator for a Good- or Bad-based ratio.", "childrenPaths": [ "$.spec.ratioMetric.total.metricSource" @@ -10185,7 +10464,7 @@ "name": "string", "kind": "string" }, - "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires Type when [SLIMetricSource.MetricSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.MetricSourceRef) is omitted. Otherwise, OpenSLO infers Type from the referenced [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource). This SDK does not validate the requirement." }, { "path": "$.spec.ratioMetric.total.metricSource.spec", @@ -10275,8 +10554,8 @@ ] } ], - "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", - "fieldDoc": "Raw retrieves a precomputed ratio.", + "typeDoc": "SLIMetricSpec defines one query used to read metric data for an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "Raw defines a query for a precomputed success or failure ratio.", "childrenPaths": [ "$.spec.ratioMetric.raw.metricSource" ] @@ -10346,7 +10625,7 @@ "name": "string", "kind": "string" }, - "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires Type when [SLIMetricSource.MetricSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.MetricSourceRef) is omitted. Otherwise, OpenSLO infers Type from the referenced [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource). This SDK does not validate the requirement." }, { "path": "$.spec.ratioMetric.raw.metricSource.spec", @@ -10527,8 +10806,14 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "Labels maps metadata keys to one or more values.", - "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "typeDoc": "Labels maps each metadata key to zero or more values. JSON decoding accepts each map value as a string or an array of strings.", + "fieldDoc": "Labels contains optional metadata associated with the object.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*", @@ -10570,6 +10855,12 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ @@ -10651,6 +10942,10 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" @@ -10786,8 +11081,17 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "Labels maps metadata keys to one or more values.", - "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", + "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] + } + ], + "typeDoc": "Labels maps each metadata key to zero or more values. JSON decoding accepts each map value as a string or an array of strings.", + "fieldDoc": "Labels contains optional metadata associated with the object.", "childrenPaths": [ "$.spec.indicator.metadata.labels.*~", "$.spec.indicator.metadata.labels.*", @@ -10832,6 +11136,15 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] + } + ], "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ @@ -10889,7 +11202,7 @@ ] } ], - "typeDoc": "SLISpec defines the metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "typeDoc": "SLISpec defines the query or queries used to calculate an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", "childrenPaths": [ "$.spec.indicator.spec.description", "$.spec.indicator.spec.thresholdMetric", @@ -10903,6 +11216,13 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "'indicator' or 'indicatorRef' is set" + ] + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", @@ -10929,8 +11249,8 @@ ] } ], - "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", - "fieldDoc": "ThresholdMetric retrieves raw values for an [SLOObjective](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective) to compare with its Operator and Value.", + "typeDoc": "SLIMetricSpec defines one query used to read metric data for an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "ThresholdMetric defines a query that returns raw values. [SLOObjective.Operator](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective.Operator) compares each value with [SLOObjective.Value](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective.Value).", "childrenPaths": [ "$.spec.indicator.spec.thresholdMetric.metricSource" ] @@ -11000,7 +11320,7 @@ "name": "string", "kind": "string" }, - "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires Type when [SLIMetricSource.MetricSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.MetricSourceRef) is omitted. Otherwise, OpenSLO infers Type from the referenced [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource). This SDK does not validate the requirement." }, { "path": "$.spec.indicator.spec.thresholdMetric.metricSource.spec", @@ -11082,7 +11402,7 @@ ] } ], - "typeDoc": "SLIRatioMetric defines a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 99 good events out of 100 produce a ratio of 0.99. One bad event out of 100 produces the same ratio.", + "typeDoc": "SLIRatioMetric defines an indicator from good divided by total or (total minus bad) divided by total. It can instead use a precomputed success or failure ratio identified by [SLIRatioMetric.RawType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIRatioMetric.RawType). For example, 99 good events out of 100 produce a ratio of 0.99. One bad event out of 100 produces the same ratio.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.counter", "$.spec.indicator.spec.ratioMetric.good", @@ -11118,7 +11438,7 @@ ] } ], - "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "typeDoc": "SLIMetricSpec defines one query used to read metric data for an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", "fieldDoc": "Good supplies the numerator for a good-over-total ratio.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.good.metricSource" @@ -11197,7 +11517,7 @@ "name": "string", "kind": "string" }, - "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires Type when [SLIMetricSource.MetricSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.MetricSourceRef) is omitted. Otherwise, OpenSLO infers Type from the referenced [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource). This SDK does not validate the requirement." }, { "path": "$.spec.indicator.spec.ratioMetric.good.metricSource.spec", @@ -11263,7 +11583,7 @@ ] } ], - "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "typeDoc": "SLIMetricSpec defines one query used to read metric data for an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", "fieldDoc": "Bad supplies the number subtracted from Total for a failure-based ratio.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.bad.metricSource" @@ -11342,7 +11662,7 @@ "name": "string", "kind": "string" }, - "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires Type when [SLIMetricSource.MetricSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.MetricSourceRef) is omitted. Otherwise, OpenSLO infers Type from the referenced [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource). This SDK does not validate the requirement." }, { "path": "$.spec.indicator.spec.ratioMetric.bad.metricSource.spec", @@ -11407,7 +11727,7 @@ ] } ], - "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "typeDoc": "SLIMetricSpec defines one query used to read metric data for an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", "fieldDoc": "Total supplies the denominator for a Good- or Bad-based ratio.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.total.metricSource" @@ -11482,7 +11802,7 @@ "name": "string", "kind": "string" }, - "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires Type when [SLIMetricSource.MetricSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.MetricSourceRef) is omitted. Otherwise, OpenSLO infers Type from the referenced [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource). This SDK does not validate the requirement." }, { "path": "$.spec.indicator.spec.ratioMetric.total.metricSource.spec", @@ -11577,8 +11897,8 @@ ] } ], - "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", - "fieldDoc": "Raw retrieves a precomputed ratio.", + "typeDoc": "SLIMetricSpec defines one query used to read metric data for an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "Raw defines a query for a precomputed success or failure ratio.", "childrenPaths": [ "$.spec.indicator.spec.ratioMetric.raw.metricSource" ] @@ -11652,7 +11972,7 @@ "name": "string", "kind": "string" }, - "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires Type when [SLIMetricSource.MetricSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.MetricSourceRef) is omitted. Otherwise, OpenSLO infers Type from the referenced [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource). This SDK does not validate the requirement." }, { "path": "$.spec.indicator.spec.ratioMetric.raw.metricSource.spec", @@ -11756,7 +12076,7 @@ "errorCode": "one_of" } ], - "typeDoc": "SLOBudgetingMethod identifies how an [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO) calculates objective success and error-budget use. Occurrences uses the ratio of good events to total events. Timeslices counts slices that meet [SLOObjective.TimeSliceTarget](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective.TimeSliceTarget). RatioTimeslices averages success ratios across slices. Composite SLOs apply objective weights to each calculation.", + "typeDoc": "SLOBudgetingMethod identifies how an [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO) aggregates SLI results for objective and error-budget evaluation. An objective's error-budget fraction is 1 minus [SLOObjective.Target](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective.Target). Its error-budget percentage is 100 minus [SLOObjective.TargetPercent](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective.TargetPercent). Occurrences uses the ratio of good events to total events. Timeslices counts slices that meet [SLOObjective.TimeSliceTarget](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective.TimeSliceTarget). RatioTimeslices averages success ratios across slices. Composite calculation rules depend on the method, as the constant comments describe.", "fieldDoc": "BudgetingMethod applies the selected error-budget calculation to every objective." }, { @@ -11772,7 +12092,7 @@ "errorCode": "slice_length" } ], - "fieldDoc": "TimeWindow contains the SLO's evaluation window." + "fieldDoc": "TimeWindow contains exactly one evaluation window. OpenSLO makes this field optional, but this SDK requires one item." }, { "path": "$.spec.timeWindow[*]", @@ -11781,7 +12101,12 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "SLOTimeWindow defines either a rolling or calendar-aligned evaluation window.", + "rules": [ + { + "description": "'calendar' must be set when 'isRolling' is false and cannot be set when 'isRolling' is true" + } + ], + "typeDoc": "SLOTimeWindow defines one rolling or calendar-aligned evaluation window. A rolling window requires [SLOTimeWindow.IsRolling](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOTimeWindow.IsRolling) to be true and [SLOTimeWindow.Calendar](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOTimeWindow.Calendar) to be nil. A calendar-aligned window requires IsRolling to be false and Calendar to be non-nil.", "childrenPaths": [ "$.spec.timeWindow[*].duration", "$.spec.timeWindow[*].isRolling", @@ -11873,7 +12198,7 @@ "kind": "[]struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "fieldDoc": "Objectives contains the SLO's target definitions." + "fieldDoc": "Objectives contains the SLO's target definitions. OpenSLO requires this field, but this SDK accepts decoded input that omits it." }, { "path": "$.spec.objectives[*]", @@ -12080,7 +12405,7 @@ } ], "typeDoc": "DurationShorthand represents a duration as an integer and a case-sensitive [DurationShorthandUnit](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DurationShorthandUnit), such as \"1m\" or \"10d\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", - "fieldDoc": "TimeSliceWindow sets the slice size and query-evaluation interval for [SLOBudgetingMethodTimeslices](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOBudgetingMethodTimeslices) and [SLOBudgetingMethodRatioTimeslices](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOBudgetingMethodRatioTimeslices)." + "fieldDoc": "TimeSliceWindow sets the slice size and query interval for [SLOBudgetingMethodTimeslices](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOBudgetingMethodTimeslices) and [SLOBudgetingMethodRatioTimeslices](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOBudgetingMethodRatioTimeslices). This Go model supports [DurationShorthand](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DurationShorthand) only. OpenSLO also permits a number, which it interprets as minutes." }, { "path": "$.spec.objectives[*].indicator", @@ -12203,8 +12528,18 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "Labels maps metadata keys to one or more values.", - "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", + "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] + } + ], + "typeDoc": "Labels maps each metadata key to zero or more values. JSON decoding accepts each map value as a string or an array of strings.", + "fieldDoc": "Labels contains optional metadata associated with the object.", "childrenPaths": [ "$.spec.objectives[*].indicator.metadata.labels.*~", "$.spec.objectives[*].indicator.metadata.labels.*", @@ -12250,6 +12585,16 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] + } + ], "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ @@ -12310,7 +12655,7 @@ ] } ], - "typeDoc": "SLISpec defines the metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "typeDoc": "SLISpec defines the query or queries used to calculate an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.description", "$.spec.objectives[*].indicator.spec.thresholdMetric", @@ -12324,6 +12669,14 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional", + "conditions": [ + "is composite SLO", + "'indicator' or 'indicatorRef' is set" + ] + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length", @@ -12352,8 +12705,8 @@ ] } ], - "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", - "fieldDoc": "ThresholdMetric retrieves raw values for an [SLOObjective](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective) to compare with its Operator and Value.", + "typeDoc": "SLIMetricSpec defines one query used to read metric data for an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "ThresholdMetric defines a query that returns raw values. [SLOObjective.Operator](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective.Operator) compares each value with [SLOObjective.Value](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOObjective.Value).", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.thresholdMetric.metricSource" ] @@ -12427,7 +12780,7 @@ "name": "string", "kind": "string" }, - "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires Type when [SLIMetricSource.MetricSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.MetricSourceRef) is omitted. Otherwise, OpenSLO infers Type from the referenced [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource). This SDK does not validate the requirement." }, { "path": "$.spec.objectives[*].indicator.spec.thresholdMetric.metricSource.spec", @@ -12515,7 +12868,7 @@ ] } ], - "typeDoc": "SLIRatioMetric defines a success ratio as good divided by total, total minus bad divided by total, or a precomputed raw ratio. For example, 99 good events out of 100 produce a ratio of 0.99. One bad event out of 100 produces the same ratio.", + "typeDoc": "SLIRatioMetric defines an indicator from good divided by total or (total minus bad) divided by total. It can instead use a precomputed success or failure ratio identified by [SLIRatioMetric.RawType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIRatioMetric.RawType). For example, 99 good events out of 100 produce a ratio of 0.99. One bad event out of 100 produces the same ratio.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.counter", "$.spec.objectives[*].indicator.spec.ratioMetric.good", @@ -12552,7 +12905,7 @@ ] } ], - "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "typeDoc": "SLIMetricSpec defines one query used to read metric data for an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", "fieldDoc": "Good supplies the numerator for a good-over-total ratio.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.good.metricSource" @@ -12635,7 +12988,7 @@ "name": "string", "kind": "string" }, - "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires Type when [SLIMetricSource.MetricSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.MetricSourceRef) is omitted. Otherwise, OpenSLO infers Type from the referenced [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource). This SDK does not validate the requirement." }, { "path": "$.spec.objectives[*].indicator.spec.ratioMetric.good.metricSource.spec", @@ -12704,7 +13057,7 @@ ] } ], - "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "typeDoc": "SLIMetricSpec defines one query used to read metric data for an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", "fieldDoc": "Bad supplies the number subtracted from Total for a failure-based ratio.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.bad.metricSource" @@ -12787,7 +13140,7 @@ "name": "string", "kind": "string" }, - "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires Type when [SLIMetricSource.MetricSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.MetricSourceRef) is omitted. Otherwise, OpenSLO infers Type from the referenced [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource). This SDK does not validate the requirement." }, { "path": "$.spec.objectives[*].indicator.spec.ratioMetric.bad.metricSource.spec", @@ -12855,7 +13208,7 @@ ] } ], - "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "typeDoc": "SLIMetricSpec defines one query used to read metric data for an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", "fieldDoc": "Total supplies the denominator for a Good- or Bad-based ratio.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.total.metricSource" @@ -12934,7 +13287,7 @@ "name": "string", "kind": "string" }, - "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires Type when [SLIMetricSource.MetricSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.MetricSourceRef) is omitted. Otherwise, OpenSLO infers Type from the referenced [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource). This SDK does not validate the requirement." }, { "path": "$.spec.objectives[*].indicator.spec.ratioMetric.total.metricSource.spec", @@ -13034,8 +13387,8 @@ ] } ], - "typeDoc": "SLIMetricSpec defines how to retrieve one metric used by an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", - "fieldDoc": "Raw retrieves a precomputed ratio.", + "typeDoc": "SLIMetricSpec defines one query used to read metric data for an [SLI](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLI).", + "fieldDoc": "Raw defines a query for a precomputed success or failure ratio.", "childrenPaths": [ "$.spec.objectives[*].indicator.spec.ratioMetric.raw.metricSource" ] @@ -13113,7 +13466,7 @@ "name": "string", "kind": "string" }, - "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires it when MetricSourceRef does not supply one. This SDK does not validate that condition." + "fieldDoc": "Type identifies the implementation-defined metric-source type. OpenSLO requires Type when [SLIMetricSource.MetricSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLIMetricSource.MetricSourceRef) is omitted. Otherwise, OpenSLO infers Type from the referenced [DataSource](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DataSource). This SDK does not validate the requirement." }, { "path": "$.spec.objectives[*].indicator.spec.ratioMetric.raw.metricSource.spec", @@ -13222,7 +13575,7 @@ ] } ], - "fieldDoc": "CompositeWeight scales this objective's contribution to a multi-objective composite SLO. OpenSLO uses a weight of 1 when omitted. This SDK leaves the field unset." + "fieldDoc": "CompositeWeight scales this objective's contribution to a composite SLO. OpenSLO permits it only with multiple objectives and defaults it to 1. This SDK does not enforce the objective-count restriction and preserves an omitted value as nil." }, { "path": "$.spec.alertPolicies", @@ -13250,7 +13603,7 @@ "errorCode": "optional" } ], - "typeDoc": "SLOAlertPolicy associates an alert policy with an [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO).", + "typeDoc": "SLOAlertPolicy supplies exactly one alert policy representation to an [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO). Set [SLOAlertPolicyInline](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOAlertPolicyInline) or [SLOAlertPolicyRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOAlertPolicyRef), but not both.", "childrenPaths": [ "$.spec.alertPolicies[*].kind", "$.spec.alertPolicies[*].metadata", @@ -13353,8 +13706,14 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "Labels maps metadata keys to one or more values.", - "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "typeDoc": "Labels maps each metadata key to zero or more values. JSON decoding accepts each map value as a string or an array of strings.", + "fieldDoc": "Labels contains optional metadata associated with the object.", "childrenPaths": [ "$.spec.alertPolicies[*].metadata.labels.*~", "$.spec.alertPolicies[*].metadata.labels.*", @@ -13396,6 +13755,12 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ @@ -13444,7 +13809,7 @@ "errorCode": "required" } ], - "typeDoc": "AlertPolicySpec defines the events that emit notifications and the condition and destinations used by an [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicy).", + "typeDoc": "AlertPolicySpec defines which condition states trigger an SLO alert and where the consuming system delivers the resulting notifications. The trigger fields have a false zero value. JSON encoding omits false values, and this SDK accepts all three fields as false.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.description", "$.spec.alertPolicies[*].spec.alertWhenNoData", @@ -13463,6 +13828,10 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" @@ -13476,7 +13845,13 @@ "name": "bool", "kind": "bool" }, - "fieldDoc": "AlertWhenNoData enables alerts when the associated SLO has no burn-rate value." + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "fieldDoc": "AlertWhenNoData reports whether to trigger an alert when the associated [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO) has no burn-rate value. Its zero value is false." }, { "path": "$.spec.alertPolicies[*].spec.alertWhenBreaching", @@ -13484,7 +13859,13 @@ "name": "bool", "kind": "bool" }, - "fieldDoc": "AlertWhenBreaching enables alerts when the condition is breaching." + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "fieldDoc": "AlertWhenBreaching reports whether to trigger an alert when the condition is breaching. Its zero value is false." }, { "path": "$.spec.alertPolicies[*].spec.alertWhenResolved", @@ -13492,7 +13873,13 @@ "name": "bool", "kind": "bool" }, - "fieldDoc": "AlertWhenResolved enables alerts when the condition resolves." + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "fieldDoc": "AlertWhenResolved reports whether to trigger an alert when the condition resolves. Its zero value is false." }, { "path": "$.spec.alertPolicies[*].spec.conditions", @@ -13507,7 +13894,7 @@ "errorCode": "slice_length" } ], - "fieldDoc": "Conditions contains the alert condition, inline or by reference." + "fieldDoc": "Conditions contains exactly one alert condition, specified inline or by reference." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*]", @@ -13526,7 +13913,7 @@ "errorCode": "optional" } ], - "typeDoc": "AlertPolicyCondition supplies an alert condition to an [AlertPolicySpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicySpec) by reference or inline definition.", + "typeDoc": "AlertPolicyCondition supplies exactly one alert condition representation to an [AlertPolicySpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicySpec). Set [AlertPolicyConditionInline](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicyConditionInline) or [AlertPolicyConditionRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicyConditionRef), but not both.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].conditionRef", "$.spec.alertPolicies[*].spec.conditions[*].kind", @@ -13559,7 +13946,7 @@ ] } ], - "fieldDoc": "ConditionRef names an existing alert condition." + "fieldDoc": "ConditionRef matches the [Metadata.Name](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#Metadata.Name) of an existing [AlertCondition](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertCondition)." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].kind", @@ -13656,8 +14043,14 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "Labels maps metadata keys to one or more values.", - "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "typeDoc": "Labels maps each metadata key to zero or more values. JSON decoding accepts each map value as a string or an array of strings.", + "fieldDoc": "Labels contains optional metadata associated with the object.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].metadata.labels.*~", "$.spec.alertPolicies[*].spec.conditions[*].metadata.labels.*", @@ -13699,6 +14092,12 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ @@ -13782,6 +14181,7 @@ } ], "typeDoc": "AlertConditionType defines a comparison against an SLO's burn rate. Burn rate is error-budget consumption relative to the rate allowed by the SLO.", + "fieldDoc": "Condition defines the burn-rate comparison used to determine whether this alert condition is breaching.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].spec.condition.kind", "$.spec.alertPolicies[*].spec.conditions[*].spec.condition.op", @@ -13898,7 +14298,7 @@ } ], "typeDoc": "DurationShorthand represents a duration as an integer and a case-sensitive [DurationShorthandUnit](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#DurationShorthandUnit), such as \"1m\" or \"10d\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", - "fieldDoc": "AlertAfter sets how long the condition must remain valid before an alert is triggered. OpenSLO treats an omitted value as \"0m\". This SDK leaves it unset." + "fieldDoc": "AlertAfter sets how long the burn-rate comparison must remain true before the condition becomes breaching. An [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicy) controls whether that state triggers an alert. OpenSLO treats an omitted value as \"0m\". This SDK leaves it unset." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.description", @@ -13907,6 +14307,10 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" @@ -13927,7 +14331,7 @@ "errorCode": "slice_min_length" } ], - "fieldDoc": "NotificationTargets lists notification destinations, inline or by reference." + "fieldDoc": "NotificationTargets contains one or more notification destinations. Specify each destination inline or by reference." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets[*]", @@ -13946,7 +14350,7 @@ "errorCode": "optional" } ], - "typeDoc": "AlertPolicyNotificationTarget supplies a notification target to an [AlertPolicySpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicySpec) by reference or inline definition.", + "typeDoc": "AlertPolicyNotificationTarget supplies exactly one notification-target representation to an [AlertPolicySpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicySpec). Set [AlertPolicyNotificationTargetInline](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicyNotificationTargetInline) or [AlertPolicyNotificationTargetRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicyNotificationTargetRef), but not both.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].targetRef", "$.spec.alertPolicies[*].spec.notificationTargets[*].kind", @@ -13979,7 +14383,7 @@ ] } ], - "fieldDoc": "TargetRef names an existing notification target." + "fieldDoc": "TargetRef matches the [Metadata.Name](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#Metadata.Name) of an existing [AlertNotificationTarget](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertNotificationTarget)." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets[*].kind", @@ -14076,8 +14480,14 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "Labels maps metadata keys to one or more values.", - "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "typeDoc": "Labels maps each metadata key to zero or more values. JSON decoding accepts each map value as a string or an array of strings.", + "fieldDoc": "Labels contains optional metadata associated with the object.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.labels.*~", "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.labels.*", @@ -14119,6 +14529,12 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ @@ -14180,12 +14596,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the destination." + "fieldDoc": "Description optionally describes the notification target." }, { "path": "$.spec.alertPolicies[*].spec.notificationTargets[*].spec.target", @@ -14199,7 +14619,7 @@ "errorCode": "required" } ], - "fieldDoc": "Target identifies the implementation-defined delivery target." + "fieldDoc": "Target specifies the notification destination in the format required by the consuming implementation. Examples include email, Slack, a webhook, and Opsgenie." }, { "path": "$.spec.alertPolicies[*].alertPolicyRef", @@ -14226,7 +14646,7 @@ ] } ], - "fieldDoc": "AlertPolicyRef names an existing alert policy." + "fieldDoc": "AlertPolicyRef matches the [Metadata.Name](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#Metadata.Name) of an existing [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicy)." } ] }, @@ -14240,7 +14660,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "Service groups related [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO) objects.", + "typeDoc": "Service identifies a high-level group of [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO) objects. An [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLO) associates with the Service by setting [SLOSpec.Service](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#SLOSpec.Service) to the Service's [Metadata.Name](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#Metadata.Name). Multiple SLOs can refer to the same Service.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -14365,8 +14785,14 @@ "kind": "map[string][]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, - "typeDoc": "Labels maps metadata keys to one or more values.", - "fieldDoc": "Labels contains user-defined metadata for describing and grouping the object.", + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], + "typeDoc": "Labels maps each metadata key to zero or more values. JSON decoding accepts each map value as a string or an array of strings.", + "fieldDoc": "Labels contains optional metadata associated with the object.", "childrenPaths": [ "$.metadata.labels.*~", "$.metadata.labels.*", @@ -14408,6 +14834,12 @@ "kind": "map[string]string", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1" }, + "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + } + ], "typeDoc": "Annotations maps metadata keys to implementation- or system-specific values.", "fieldDoc": "Annotations contains implementation- or system-specific metadata.", "childrenPaths": [ @@ -14462,6 +14894,10 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" @@ -14483,7 +14919,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha" }, - "typeDoc": "SLO defines reliability targets for a service level measured by an indicator.", + "typeDoc": "SLO is the legacy v1alpha SLO representation supported by this SDK. It defines reliability targets for a service level measured by an indicator.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -14641,7 +15077,7 @@ "errorCode": "slice_length" } ], - "fieldDoc": "TimeWindows contains the SLO evaluation window." + "fieldDoc": "TimeWindows contains exactly one SLO evaluation window." }, { "path": "$.spec.timeWindows[*]", @@ -14650,7 +15086,12 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha" }, - "typeDoc": "SLOTimeWindow defines the period over which an SLO is evaluated. For example, a Unit of Week and a Count of 4 define a four-week window.", + "rules": [ + { + "description": "'calendar' must be set when 'isRolling' is false and cannot be set when 'isRolling' is true" + } + ], + "typeDoc": "SLOTimeWindow defines the period over which an SLO is evaluated. For example, a Unit of Week and a Count of 4 define a four-week window. A rolling window requires IsRolling to be true and Calendar to be nil. A calendar-aligned window requires IsRolling to be false and Calendar to be non-nil.", "childrenPaths": [ "$.spec.timeWindows[*].unit", "$.spec.timeWindows[*].count", @@ -14721,7 +15162,7 @@ } ], "typeDoc": "SLOCalendar anchors a calendar-aligned [SLOTimeWindow](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha#SLOTimeWindow).", - "fieldDoc": "Calendar defines the alignment of a calendar window.", + "fieldDoc": "Calendar defines the alignment when IsRolling is false. It must be nil when IsRolling is true.", "childrenPaths": [ "$.spec.timeWindows[*].calendar.startTime", "$.spec.timeWindows[*].calendar.timeZone" @@ -14792,12 +15233,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the SLO." + "fieldDoc": "Description is an optional summary of the SLO." }, { "path": "$.spec.indicator", @@ -14813,7 +15258,7 @@ } ], "typeDoc": "SLOIndicator defines the threshold-metric form of a v1alpha service level indicator.", - "fieldDoc": "Indicator defines the threshold-metric form of the SLO.", + "fieldDoc": "Indicator defines the threshold-metric form of the SLO. It must be nil for the ratio form.", "childrenPaths": [ "$.spec.indicator.thresholdMetric" ] @@ -14905,7 +15350,7 @@ "errorCode": "required" } ], - "fieldDoc": "Service identifies the service whose reliability the SLO measures." + "fieldDoc": "Service is the metadata name of the [Service](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha#Service) whose reliability the SLO measures." }, { "path": "$.spec.objectives", @@ -14914,7 +15359,7 @@ "kind": "[]struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha" }, - "fieldDoc": "Objectives contains the targets used to evaluate the indicator." + "fieldDoc": "Objectives contains reliability targets. For the ratio form, each objective's [SLOObjective.RatioMetrics](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha#SLOObjective.RatioMetrics) defines the SLI metric queries." }, { "path": "$.spec.objectives[*]", @@ -15247,7 +15692,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha" }, - "typeDoc": "Service groups related SLOs.", + "typeDoc": "Service is the legacy v1alpha Service representation supported by this SDK. SLOs reference it through [SLOSpec.Service](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1alpha#SLOSpec.Service), and multiple SLOs can reference the same Service.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -15382,12 +15827,16 @@ "kind": "string" }, "rules": [ + { + "description": "property is optional", + "errorCode": "optional" + }, { "description": "length must be less than or equal to 1050", "errorCode": "string_max_length" } ], - "fieldDoc": "Description summarizes the service." + "fieldDoc": "Description is an optional summary of the service." } ] } diff --git a/go.work.sum b/go.work.sum index 7639051..2ec4f1b 100644 --- a/go.work.sum +++ b/go.work.sum @@ -42,6 +42,7 @@ golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -51,8 +52,6 @@ golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= -golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -70,6 +69,7 @@ golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXct golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4= golang.org/x/telemetry v0.0.0-20260708182218-49f421fb7959/go.mod h1:LV7u5Oco+Z/g6XI7PqN+EUUUGGkEcmB1uj2ceI0fOVg= +golang.org/x/telemetry v0.0.0-20260811182544-a038080d80e5/go.mod h1:LVehoXe41cL5SCVQilsV7Gg6BNG+Js6P9PhSbYTIUkQ= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= diff --git a/internal/cmd/objectdoc/main_test.go b/internal/cmd/objectdoc/main_test.go index 2810a1d..5427773 100644 --- a/internal/cmd/objectdoc/main_test.go +++ b/internal/cmd/objectdoc/main_test.go @@ -268,37 +268,37 @@ func TestNormalizeGeneratedDocsRecoversPromotedFieldDocs(t *testing.T) { name: "v1 conditionRef", doc: docs[0].doc, path: "$.spec.conditions[*].conditionRef", - fieldDoc: "ConditionRef names an existing alert condition.", + fieldDoc: "ConditionRef matches the [Metadata.Name](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#Metadata.Name) of an existing [AlertCondition](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertCondition).", }, { name: "v1 targetRef", doc: docs[0].doc, path: "$.spec.notificationTargets[*].targetRef", - fieldDoc: "TargetRef names an existing notification target.", + fieldDoc: "TargetRef matches the [Metadata.Name](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#Metadata.Name) of an existing [AlertNotificationTarget](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertNotificationTarget).", }, { name: "v1 alertPolicyRef", doc: docs[1].doc, path: "$.spec.alertPolicies[*].alertPolicyRef", - fieldDoc: "AlertPolicyRef names an existing alert policy.", + fieldDoc: "AlertPolicyRef matches the [Metadata.Name](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#Metadata.Name) of an existing [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicy).", }, { name: "v2alpha conditionRef", doc: docs[2].doc, path: "$.spec.conditions[*].conditionRef", - fieldDoc: "ConditionRef names the alert condition to use.", + fieldDoc: "ConditionRef is the metadata name of the alert condition to use.", }, { name: "v2alpha targetRef", doc: docs[2].doc, path: "$.spec.notificationTargets[*].targetRef", - fieldDoc: "TargetRef names the notification target to use.", + fieldDoc: "TargetRef is the metadata name of the notification target to use.", }, { name: "v2alpha alertPolicyRef", doc: docs[3].doc, path: "$.spec.alertPolicies[*].alertPolicyRef", - fieldDoc: "AlertPolicyRef names the alert policy to use.", + fieldDoc: "AlertPolicyRef is the metadata name of the alert policy to use.", }, } for _, test := range tests { @@ -383,6 +383,67 @@ func TestRecoverFieldDocsRejectsOriginlessRealPaths(t *testing.T) { ) } +func TestGeneratedDocsExposeCrossCuttingContracts(t *testing.T) { + versions, err := generateVersions() + require.NoError(t, err) + + for version, documents := range versions { + for kind, document := range documents { + t.Run(version+"/"+kind+"/description", func(t *testing.T) { + requirePropertyRule( + t, + requireProperty(t, document, jsonpath.Parse("$.spec.description")), + govy.ErrorCode("optional"), + ) + }) + } + } + + for _, version := range []Version{"openslo/v1", "openslo.com/v2alpha"} { + for kind, document := range versions[version] { + for _, path := range []string{"$.metadata.labels", "$.metadata.annotations"} { + t.Run(version+"/"+kind+"/"+path, func(t *testing.T) { + requirePropertyRule( + t, + requireProperty(t, document, jsonpath.Parse(path)), + govy.ErrorCode("optional"), + ) + }) + } + } + + policy := versions[version]["AlertPolicy"] + for _, path := range []string{ + "$.spec.alertWhenNoData", + "$.spec.alertWhenBreaching", + "$.spec.alertWhenResolved", + } { + t.Run(version+"/AlertPolicy/"+path, func(t *testing.T) { + requirePropertyRule( + t, + requireProperty(t, policy, jsonpath.Parse(path)), + govy.ErrorCode("optional"), + ) + }) + } + } + + timeWindowPaths := map[Version]string{ + "openslo/v1alpha": "$.spec.timeWindows[*]", + "openslo/v1": "$.spec.timeWindow[*]", + "openslo.com/v2alpha": "$.spec.timeWindow[*]", + } + for version, path := range timeWindowPaths { + t.Run(version+"/SLO/calendar invariant", func(t *testing.T) { + requirePropertyRuleDescription( + t, + requireProperty(t, versions[version]["SLO"], jsonpath.Parse(path)), + "'calendar' must be set when 'isRolling' is false and cannot be set when 'isRolling' is true", + ) + }) + } +} + func TestGenerateVersionsMatchesCanonicalManifest(t *testing.T) { first, err := generateVersions() require.NoError(t, err) @@ -502,3 +563,41 @@ func findProperty(doc govydoc.ObjectDoc, path jsonpath.Path) *govydoc.PropertyDo } return nil } + +func requirePropertyRule( + t *testing.T, + property *govydoc.PropertyDoc, + errorCode govy.ErrorCode, + conditions ...string, +) { + t.Helper() + for _, rule := range property.Rules { + if rule.ErrorCode == errorCode && slices.Equal(rule.Conditions, conditions) { + return + } + } + t.Fatalf( + "property %s has no %q rule with conditions %v", + property.Path, + errorCode, + conditions, + ) +} + +func requirePropertyRuleDescription( + t *testing.T, + property *govydoc.PropertyDoc, + description string, +) { + t.Helper() + for _, rule := range property.Rules { + if rule.Description == description { + return + } + } + t.Fatalf( + "property %s has no rule with description %q", + property.Path, + description, + ) +} diff --git a/pkg/openslo/v1/alert_condition.go b/pkg/openslo/v1/alert_condition.go index d7fb737..c03a68e 100644 --- a/pkg/openslo/v1/alert_condition.go +++ b/pkg/openslo/v1/alert_condition.go @@ -23,8 +23,8 @@ func NewAlertCondition(metadata Metadata, spec AlertConditionSpec) AlertConditio } } -// AlertCondition defines the condition under which an SLO alert is considered -// breaching. +// AlertCondition defines a burn-rate condition for an SLO. An [AlertPolicy] +// controls whether a breaching condition triggers an alert. type AlertCondition struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -52,7 +52,8 @@ func (a AlertCondition) Validate() error { return alertConditionValidation.Validate(a) } -// String returns the AlertCondition's formatted version, kind, and name. +// String returns the alert condition's formatted version and kind. It also +// returns [Metadata.Name] when set. func (a AlertCondition) String() string { return internal.GetObjectName(a) } @@ -70,7 +71,9 @@ func (a AlertCondition) GetValidator() govy.Validator[AlertCondition] { // AlertConditionSpec defines an alert's severity and burn-rate condition. type AlertConditionSpec struct { // Severity is an implementation-defined classification such as "sev1" or "page". - Severity string `json:"severity"` + Severity string `json:"severity"` + // Condition defines the burn-rate comparison used to determine whether this + // alert condition is breaching. Condition AlertConditionType `json:"condition"` // Description summarizes the alert condition. Description string `json:"description,omitempty"` @@ -88,8 +91,10 @@ type AlertConditionType struct { Threshold *float64 `json:"threshold"` // LookbackWindow sets the period for burn-rate calculation. LookbackWindow DurationShorthand `json:"lookbackWindow"` - // AlertAfter sets how long the condition must remain valid before an alert is - // triggered. OpenSLO treats an omitted value as "0m". This SDK leaves it unset. + // AlertAfter sets how long the burn-rate comparison must remain true before + // the condition becomes breaching. An [AlertPolicy] controls whether that + // state triggers an alert. OpenSLO treats an omitted value as "0m". This SDK + // leaves it unset. AlertAfter *DurationShorthand `json:"alertAfter,omitempty"` } @@ -114,6 +119,7 @@ var alertConditionValidation = govy.New( var alertConditionSpecValidation = govy.New( govy.For(func(spec AlertConditionSpec) string { return spec.Description }). WithName("description"). + OmitEmpty(). Rules(rules.StringMaxLength(1050)), govy.For(func(spec AlertConditionSpec) string { return spec.Severity }). WithName("severity"). diff --git a/pkg/openslo/v1/alert_notification_target.go b/pkg/openslo/v1/alert_notification_target.go index 680c7a2..b602566 100644 --- a/pkg/openslo/v1/alert_notification_target.go +++ b/pkg/openslo/v1/alert_notification_target.go @@ -51,7 +51,8 @@ func (a AlertNotificationTarget) Validate() error { return alertNotificationTargetValidation.Validate(a) } -// String returns the target's formatted version, kind, and name. +// String returns the target's formatted version and kind. It also returns +// [Metadata.Name] when set. func (a AlertNotificationTarget) String() string { return internal.GetObjectName(a) } @@ -61,7 +62,7 @@ func (a AlertNotificationTarget) GetMetadata() Metadata { return a.Metadata } -// GetValidator returns the validator for AlertNotificationTarget objects. +// GetValidator returns the validator for [AlertNotificationTarget] objects. func (a AlertNotificationTarget) GetValidator() govy.Validator[AlertNotificationTarget] { return alertNotificationTargetValidation } @@ -69,9 +70,10 @@ func (a AlertNotificationTarget) GetValidator() govy.Validator[AlertNotification // AlertNotificationTargetSpec defines an implementation-specific notification // destination. type AlertNotificationTargetSpec struct { - // Description summarizes the destination. + // Description optionally describes the notification target. Description string `json:"description,omitempty"` - // Target identifies the implementation-defined delivery target. + // Target specifies the notification destination in the format required by the + // consuming implementation. Examples include email, Slack, a webhook, and Opsgenie. Target string `json:"target"` } @@ -95,5 +97,6 @@ var alertNotificationTargetSpecValidation = govy.New( Required(), govy.For(func(spec AlertNotificationTargetSpec) string { return spec.Description }). WithName("description"). + OmitEmpty(). Rules(rules.StringMaxLength(1050)), ) diff --git a/pkg/openslo/v1/alert_policy.go b/pkg/openslo/v1/alert_policy.go index 1fcda1f..7f3e260 100644 --- a/pkg/openslo/v1/alert_policy.go +++ b/pkg/openslo/v1/alert_policy.go @@ -23,8 +23,8 @@ func NewAlertPolicy(metadata Metadata, spec AlertPolicySpec) AlertPolicy { } } -// AlertPolicy defines when the system emits alerts for an SLO and where it sends -// them. +// AlertPolicy defines which alert-condition states trigger an SLO alert. It also +// defines where the consuming system delivers the resulting notifications. // It is distinct from an organization's policy for responding to error-budget // consumption. type AlertPolicy struct { @@ -54,7 +54,8 @@ func (a AlertPolicy) Validate() error { return alertPolicyValidation.Validate(a) } -// String returns the AlertPolicy's formatted version, kind, and name. +// String returns the alert policy's formatted version and kind. It also returns +// [Metadata.Name] when set. func (a AlertPolicy) String() string { return internal.GetObjectName(a) } @@ -69,64 +70,75 @@ func (a AlertPolicy) GetValidator() govy.Validator[AlertPolicy] { return alertPolicyValidation } -// AlertPolicySpec defines the events that emit notifications and the condition -// and destinations used by an [AlertPolicy]. +// AlertPolicySpec defines which condition states trigger an SLO alert and where +// the consuming system delivers the resulting notifications. +// The trigger fields have a false zero value. JSON encoding omits false values, +// and this SDK accepts all three fields as false. type AlertPolicySpec struct { // Description summarizes the alert policy. Description string `json:"description,omitempty"` - // AlertWhenNoData enables alerts when the associated SLO has no burn-rate - // value. + // AlertWhenNoData reports whether to trigger an alert when the associated + // [SLO] has no burn-rate value. Its zero value is false. AlertWhenNoData bool `json:"alertWhenNoData,omitempty"` - // AlertWhenBreaching enables alerts when the condition is breaching. + // AlertWhenBreaching reports whether to trigger an alert when the condition is + // breaching. Its zero value is false. AlertWhenBreaching bool `json:"alertWhenBreaching,omitempty"` - // AlertWhenResolved enables alerts when the condition resolves. + // AlertWhenResolved reports whether to trigger an alert when the condition + // resolves. Its zero value is false. AlertWhenResolved bool `json:"alertWhenResolved,omitempty"` - // Conditions contains the alert condition, inline or by reference. + // Conditions contains exactly one alert condition, specified inline or by + // reference. Conditions []AlertPolicyCondition `json:"conditions,omitempty"` - // NotificationTargets lists notification destinations, inline or by reference. + // NotificationTargets contains one or more notification destinations. Specify + // each destination inline or by reference. NotificationTargets []AlertPolicyNotificationTarget `json:"notificationTargets,omitempty"` } -// AlertPolicyCondition supplies an alert condition to an [AlertPolicySpec] by -// reference or inline definition. +// AlertPolicyCondition supplies exactly one alert condition representation to an +// [AlertPolicySpec]. Set [AlertPolicyConditionInline] or +// [AlertPolicyConditionRef], but not both. type AlertPolicyCondition struct { *AlertPolicyConditionRef *AlertPolicyConditionInline } -// AlertPolicyConditionInline is an [AlertCondition] embedded in an -// [AlertPolicy]. +// AlertPolicyConditionInline is the inline form of an [AlertCondition]. It omits +// [AlertCondition.APIVersion]. type AlertPolicyConditionInline struct { Kind openslo.Kind `json:"kind"` Metadata Metadata `json:"metadata"` Spec AlertConditionSpec `json:"spec"` } -// AlertPolicyConditionRef refers to an existing [AlertCondition]. +// AlertPolicyConditionRef identifies an existing [AlertCondition] by +// [Metadata.Name]. type AlertPolicyConditionRef struct { - // ConditionRef names an existing alert condition. + // ConditionRef matches the [Metadata.Name] of an existing [AlertCondition]. ConditionRef string `json:"conditionRef"` } -// AlertPolicyNotificationTarget supplies a notification target to an -// [AlertPolicySpec] by reference or inline definition. +// AlertPolicyNotificationTarget supplies exactly one notification-target +// representation to an [AlertPolicySpec]. Set +// [AlertPolicyNotificationTargetInline] or [AlertPolicyNotificationTargetRef], +// but not both. type AlertPolicyNotificationTarget struct { *AlertPolicyNotificationTargetRef *AlertPolicyNotificationTargetInline } -// AlertPolicyNotificationTargetInline is an [AlertNotificationTarget] embedded -// in an [AlertPolicy]. +// AlertPolicyNotificationTargetInline is the inline form of an +// [AlertNotificationTarget]. It omits [AlertNotificationTarget.APIVersion]. type AlertPolicyNotificationTargetInline struct { Kind openslo.Kind `json:"kind"` Metadata Metadata `json:"metadata"` Spec AlertNotificationTargetSpec `json:"spec"` } -// AlertPolicyNotificationTargetRef refers to an existing -// [AlertNotificationTarget]. +// AlertPolicyNotificationTargetRef identifies an existing +// [AlertNotificationTarget] by [Metadata.Name]. type AlertPolicyNotificationTargetRef struct { - // TargetRef names an existing notification target. + // TargetRef matches the [Metadata.Name] of an existing + // [AlertNotificationTarget]. TargetRef string `json:"targetRef"` } @@ -142,7 +154,17 @@ var alertPolicyValidation = govy.New( var alertPolicySpecValidation = govy.New( govy.For(func(spec AlertPolicySpec) string { return spec.Description }). WithName("description"). + OmitEmpty(). Rules(rules.StringMaxLength(1050)), + govy.For(func(spec AlertPolicySpec) bool { return spec.AlertWhenNoData }). + WithName("alertWhenNoData"). + OmitEmpty(), + govy.For(func(spec AlertPolicySpec) bool { return spec.AlertWhenBreaching }). + WithName("alertWhenBreaching"). + OmitEmpty(), + govy.For(func(spec AlertPolicySpec) bool { return spec.AlertWhenResolved }). + WithName("alertWhenResolved"). + OmitEmpty(), govy.ForSlice(func(spec AlertPolicySpec) []AlertPolicyCondition { return spec.Conditions }). WithName("conditions"). Rules(rules.SliceLength[[]AlertPolicyCondition](1, 1)). diff --git a/pkg/openslo/v1/data_source.go b/pkg/openslo/v1/data_source.go index 959898d..dfb47f8 100644 --- a/pkg/openslo/v1/data_source.go +++ b/pkg/openslo/v1/data_source.go @@ -25,7 +25,11 @@ func NewDataSource(metadata Metadata, spec DataSourceSpec) DataSource { } } -// DataSource stores reusable connection details for a metrics backend. +// DataSource represents reusable connection details for a metric source. +// [SLIMetricSource.MetricSourceRef] selects it by [Metadata.Name]. A referenced +// metric source can omit [SLIMetricSource.Type] and keep connection details, +// such as authentication settings, outside the [SLI]. An SLI can instead define +// an inline metric source with [SLIMetricSource.Type] and [SLIMetricSource.Spec]. type DataSource struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -53,7 +57,8 @@ func (d DataSource) Validate() error { return dataSourceValidation.Validate(d) } -// String returns the data source's formatted version, kind, and name. +// String returns the data source's formatted version and kind. It also returns +// [Metadata.Name] when set. func (d DataSource) String() string { return internal.GetObjectName(d) } @@ -72,9 +77,12 @@ func (d DataSource) GetValidator() govy.Validator[DataSource] { type DataSourceSpec struct { // Description summarizes the data source. Description string `json:"description,omitempty"` - // Type names the implementation-defined metric source. + // Type identifies the implementation-defined metric source type, such as + // Prometheus or Datadog. Type string `json:"type"` - // ConnectionDetails stores source-specific connection configuration as JSON. + // ConnectionDetails contains implementation-defined connection data encoded + // as JSON. The metric-source implementation defines its fields, which can + // include endpoints or authentication settings. ConnectionDetails json.RawMessage `json:"connectionDetails"` } @@ -87,6 +95,7 @@ var dataSourceValidation = govy.New( Include(govy.New( govy.For(func(spec DataSourceSpec) string { return spec.Description }). WithName("description"). + OmitEmpty(). Rules(rules.StringMaxLength(1050)), govy.For(func(spec DataSourceSpec) string { return spec.Type }). WithName("type"). diff --git a/pkg/openslo/v1/objects.go b/pkg/openslo/v1/objects.go index 5e8ae90..a1bde14 100644 --- a/pkg/openslo/v1/objects.go +++ b/pkg/openslo/v1/objects.go @@ -41,13 +41,14 @@ type Metadata struct { Name string `json:"name"` // DisplayName is the object's human-readable name. DisplayName string `json:"displayName,omitempty"` - // Labels contains user-defined metadata for describing and grouping the object. + // Labels contains optional metadata associated with the object. Labels Labels `json:"labels,omitempty"` // Annotations contains implementation- or system-specific metadata. Annotations Annotations `json:"annotations,omitempty"` } -// Labels maps metadata keys to one or more values. +// Labels maps each metadata key to zero or more values. JSON decoding accepts +// each map value as a string or an array of strings. type Labels map[string]Label // Annotations maps metadata keys to implementation- or system-specific values. @@ -137,9 +138,11 @@ func validationRulesMetadata[T any](getter func(T) Metadata) govy.PropertyRules[ Rules(rules.StringMaxLength(63)), govy.For(func(m Metadata) Labels { return m.Labels }). WithName("labels"). + OmitEmpty(). Include(labelsValidator()), govy.For(func(m Metadata) Annotations { return m.Annotations }). WithName("annotations"). + OmitEmpty(). Include(annotationsValidator()), ), ) diff --git a/pkg/openslo/v1/service.go b/pkg/openslo/v1/service.go index 2b2c4f2..c8440c3 100644 --- a/pkg/openslo/v1/service.go +++ b/pkg/openslo/v1/service.go @@ -23,7 +23,9 @@ func NewService(metadata Metadata, spec ServiceSpec) Service { } } -// Service groups related [SLO] objects. +// Service identifies a high-level group of [SLO] objects. An [SLO] associates +// with the Service by setting [SLOSpec.Service] to the Service's [Metadata.Name]. +// Multiple SLOs can refer to the same Service. type Service struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -51,7 +53,8 @@ func (s Service) Validate() error { return serviceValidation.Validate(s) } -// String returns the Service's formatted version, kind, and name. +// String returns the service's formatted version and kind. It also returns +// [Metadata.Name] when set. func (s Service) String() string { return internal.GetObjectName(s) } @@ -81,6 +84,7 @@ var serviceValidation = govy.New( Include(govy.New( govy.For(func(spec ServiceSpec) string { return spec.Description }). WithName("description"). + OmitEmpty(). Rules(rules.StringMaxLength(1050)), )), ).WithNameFunc(internal.GetObjectName[Service]) diff --git a/pkg/openslo/v1/sli.go b/pkg/openslo/v1/sli.go index 6136ffb..067fb17 100644 --- a/pkg/openslo/v1/sli.go +++ b/pkg/openslo/v1/sli.go @@ -23,7 +23,8 @@ func NewSLI(metadata Metadata, spec SLISpec) SLI { } } -// SLI describes how to read metric data used to evaluate an [SLO]. +// SLI defines a derived reliability indicator and the queries used to calculate +// it for an [SLO]. type SLI struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -51,7 +52,8 @@ func (s SLI) Validate() error { return sliValidation.Validate(s) } -// String returns the SLI's formatted version, kind, and name. +// String returns the SLI's formatted version and kind. It also returns +// [Metadata.Name] when set. func (s SLI) String() string { return internal.GetObjectName(s) } @@ -66,19 +68,20 @@ func (s SLI) GetValidator() govy.Validator[SLI] { return sliValidation } -// SLISpec defines the metric used by an [SLI]. +// SLISpec defines the query or queries used to calculate an [SLI]. type SLISpec struct { // Description summarizes the SLI. Description string `json:"description,omitempty"` - // ThresholdMetric retrieves raw values for an [SLOObjective] to compare with - // its Operator and Value. + // ThresholdMetric defines a query that returns raw values. + // [SLOObjective.Operator] compares each value with [SLOObjective.Value]. ThresholdMetric *SLIMetricSpec `json:"thresholdMetric,omitempty"` RatioMetric *SLIRatioMetric `json:"ratioMetric,omitempty"` } -// SLIRatioMetric defines a success ratio as good divided by total, total minus -// bad divided by total, or a precomputed raw ratio. For example, 99 good events -// out of 100 produce a ratio of 0.99. One bad event out of 100 produces the same +// SLIRatioMetric defines an indicator from good divided by total or (total minus +// bad) divided by total. It can instead use a precomputed success or failure +// ratio identified by [SLIRatioMetric.RawType]. For example, 99 good events out +// of 100 produce a ratio of 0.99. One bad event out of 100 produces the same // ratio. type SLIRatioMetric struct { // Counter reports whether the queried good, bad, and total metrics are @@ -93,11 +96,11 @@ type SLIRatioMetric struct { // RawType selects whether Raw is interpreted as a success or failure ratio // when Raw is used. RawType SLIRawMetricType `json:"rawType,omitempty"` - // Raw retrieves a precomputed ratio. + // Raw defines a query for a precomputed success or failure ratio. Raw *SLIMetricSpec `json:"raw,omitempty"` } -// SLIMetricSpec defines how to retrieve one metric used by an [SLI]. +// SLIMetricSpec defines one query used to read metric data for an [SLI]. type SLIMetricSpec struct { MetricSource SLIMetricSource `json:"metricSource"` } @@ -108,8 +111,9 @@ type SLIMetricSource struct { // MetricSourceRef names an existing [DataSource]. MetricSourceRef string `json:"metricSourceRef,omitempty"` // Type identifies the implementation-defined metric-source type. - // OpenSLO requires it when MetricSourceRef does not supply one. This SDK does - // not validate that condition. + // OpenSLO requires Type when [SLIMetricSource.MetricSourceRef] is omitted. + // Otherwise, OpenSLO infers Type from the referenced [DataSource]. This SDK + // does not validate the requirement. Type string `json:"type,omitempty"` // Spec contains source-specific query or metric-retrieval configuration. Spec map[string]any `json:"spec"` @@ -142,6 +146,7 @@ var sliValidation = govy.New( var sliSpecValidation = govy.New( govy.For(func(spec SLISpec) string { return spec.Description }). WithName("description"). + OmitEmpty(). Rules(rules.StringMaxLength(1050)), govy.For(govy.GetSelf[SLISpec]()). Rules(rules.MutuallyExclusive(true, map[string]func(s SLISpec) any{ diff --git a/pkg/openslo/v1/slo.go b/pkg/openslo/v1/slo.go index a951c8d..f71f975 100644 --- a/pkg/openslo/v1/slo.go +++ b/pkg/openslo/v1/slo.go @@ -55,7 +55,8 @@ func (s SLO) Validate() error { return sloValidation.Validate(s) } -// String returns the SLO's formatted version, kind, and name. +// String returns the SLO's formatted version and kind. It also returns +// [Metadata.Name] when set. func (s SLO) String() string { return internal.GetObjectName(s) } @@ -92,9 +93,11 @@ type SLOSpec struct { // BudgetingMethod applies the selected error-budget calculation to every // objective. BudgetingMethod SLOBudgetingMethod `json:"budgetingMethod"` - // TimeWindow contains the SLO's evaluation window. + // TimeWindow contains exactly one evaluation window. OpenSLO makes this field + // optional, but this SDK requires one item. TimeWindow []SLOTimeWindow `json:"timeWindow,omitempty"` - // Objectives contains the SLO's target definitions. + // Objectives contains the SLO's target definitions. OpenSLO requires this + // field, but this SDK accepts decoded input that omits it. Objectives []SLOObjective `json:"objectives"` // AlertPolicies contains inline alert policies or references to existing // [AlertPolicy] objects. @@ -112,12 +115,14 @@ func (s SLOSpec) HasCompositeObjectives() bool { return false } -// SLOBudgetingMethod identifies how an [SLO] calculates objective success and -// error-budget use. +// SLOBudgetingMethod identifies how an [SLO] aggregates SLI results for +// objective and error-budget evaluation. An objective's error-budget fraction +// is 1 minus [SLOObjective.Target]. Its error-budget percentage is 100 minus +// [SLOObjective.TargetPercent]. // Occurrences uses the ratio of good events to total events. Timeslices counts // slices that meet [SLOObjective.TimeSliceTarget]. RatioTimeslices averages -// success ratios across slices. Composite SLOs apply objective weights to each -// calculation. +// success ratios across slices. Composite calculation rules depend on the +// method, as the constant comments describe. type SLOBudgetingMethod string const ( @@ -165,20 +170,26 @@ type SLOObjective struct { // TimeSliceTarget classifies a slice as good when BudgetingMethod is // [SLOBudgetingMethodTimeslices]. TimeSliceTarget *float64 `json:"timeSliceTarget,omitempty"` - // TimeSliceWindow sets the slice size and query-evaluation interval for + // TimeSliceWindow sets the slice size and query interval for // [SLOBudgetingMethodTimeslices] and [SLOBudgetingMethodRatioTimeslices]. + // This Go model supports [DurationShorthand] only. OpenSLO also permits a + // number, which it interprets as minutes. TimeSliceWindow *DurationShorthand `json:"timeSliceWindow,omitempty"` // Indicator defines this objective's SLI inline for a composite SLO. Indicator *SLOIndicatorInline `json:"indicator,omitempty"` // IndicatorRef names this objective's [SLI] for a composite SLO. IndicatorRef *string `json:"indicatorRef,omitempty"` - // CompositeWeight scales this objective's contribution to a multi-objective - // composite SLO. OpenSLO uses a weight of 1 when omitted. This SDK leaves the - // field unset. + // CompositeWeight scales this objective's contribution to a composite SLO. + // OpenSLO permits it only with multiple objectives and defaults it to 1. This + // SDK does not enforce the objective-count restriction and preserves an omitted + // value as nil. CompositeWeight *float64 `json:"compositeWeight,omitempty"` } -// SLOTimeWindow defines either a rolling or calendar-aligned evaluation window. +// SLOTimeWindow defines one rolling or calendar-aligned evaluation window. A +// rolling window requires [SLOTimeWindow.IsRolling] to be true and +// [SLOTimeWindow.Calendar] to be nil. A calendar-aligned window requires +// IsRolling to be false and Calendar to be non-nil. type SLOTimeWindow struct { // Duration is the length of the evaluation window. Duration DurationShorthand `json:"duration"` @@ -197,22 +208,24 @@ type SLOCalendar struct { TimeZone string `json:"timeZone"` } -// SLOAlertPolicy associates an alert policy with an [SLO]. +// SLOAlertPolicy supplies exactly one alert policy representation to an [SLO]. +// Set [SLOAlertPolicyInline] or [SLOAlertPolicyRef], but not both. type SLOAlertPolicy struct { *SLOAlertPolicyInline *SLOAlertPolicyRef } -// SLOAlertPolicyInline is an [AlertPolicy] embedded in an [SLO]. +// SLOAlertPolicyInline is the inline form of an [AlertPolicy]. It omits +// [AlertPolicy.APIVersion]. type SLOAlertPolicyInline struct { Kind openslo.Kind `json:"kind"` Metadata Metadata `json:"metadata"` Spec AlertPolicySpec `json:"spec"` } -// SLOAlertPolicyRef refers to an existing [AlertPolicy]. +// SLOAlertPolicyRef identifies an existing [AlertPolicy] by [Metadata.Name]. type SLOAlertPolicyRef struct { - // AlertPolicyRef names an existing alert policy. + // AlertPolicyRef matches the [Metadata.Name] of an existing [AlertPolicy]. AlertPolicyRef string `json:"alertPolicyRef"` } @@ -239,6 +252,7 @@ var sloSpecValidation = govy.New( ), govy.For(func(spec SLOSpec) string { return spec.Description }). WithName("description"). + OmitEmpty(). Rules(rules.StringMaxLength(1050)), govy.For(func(spec SLOSpec) string { return spec.Service }). WithName("service"). @@ -319,7 +333,9 @@ var sloTimeWindowValidation = govy.New( return govy.NewRuleError("'calendar' must be set when 'isRolling' is false") } return nil - })), + }).WithDescription( + "'calendar' must be set when 'isRolling' is false and cannot be set when 'isRolling' is true", + )), govy.For(func(t SLOTimeWindow) DurationShorthand { return t.Duration }). WithName("duration"). Required(). diff --git a/pkg/openslo/v1alpha/doc.go b/pkg/openslo/v1alpha/doc.go index ab64bdf..d5229f3 100644 --- a/pkg/openslo/v1alpha/doc.go +++ b/pkg/openslo/v1alpha/doc.go @@ -1,2 +1,4 @@ -// Package v1alpha defines OpenSLO v1alpha objects and supporting types. +// Package v1alpha defines the legacy v1alpha representation supported by this +// SDK. The exported fields, JSON tags, and validators define this package's +// contract. package v1alpha diff --git a/pkg/openslo/v1alpha/service.go b/pkg/openslo/v1alpha/service.go index d8d5164..d7af61a 100644 --- a/pkg/openslo/v1alpha/service.go +++ b/pkg/openslo/v1alpha/service.go @@ -23,7 +23,9 @@ func NewService(metadata Metadata, spec ServiceSpec) Service { } } -// Service groups related SLOs. +// Service is the legacy v1alpha Service representation supported by this SDK. +// SLOs reference it through [SLOSpec.Service], and multiple SLOs can reference +// the same Service. type Service struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -51,7 +53,8 @@ func (s Service) Validate() error { return serviceValidation.Validate(s) } -// String returns the qualified object name. +// String returns the service's formatted version and kind. It also returns +// [Metadata.Name] when set. func (s Service) String() string { return internal.GetObjectName(s) } @@ -68,7 +71,7 @@ func (s Service) GetValidator() govy.Validator[Service] { // ServiceSpec contains the descriptive properties of a [Service]. type ServiceSpec struct { - // Description summarizes the service. + // Description is an optional summary of the service. Description string `json:"description,omitempty"` } @@ -81,6 +84,7 @@ var serviceValidation = govy.New( Include(govy.New( govy.For(func(spec ServiceSpec) string { return spec.Description }). WithName("description"). + OmitEmpty(). Rules(rules.StringMaxLength(1050)), )), ).WithNameFunc(internal.GetObjectName[Service]) diff --git a/pkg/openslo/v1alpha/slo.go b/pkg/openslo/v1alpha/slo.go index 64cc822..a9ed86f 100644 --- a/pkg/openslo/v1alpha/slo.go +++ b/pkg/openslo/v1alpha/slo.go @@ -26,7 +26,8 @@ func NewSLO(metadata Metadata, spec SLOSpec) SLO { } } -// SLO defines reliability targets for a service level measured by an indicator. +// SLO is the legacy v1alpha SLO representation supported by this SDK. It +// defines reliability targets for a service level measured by an indicator. type SLO struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -54,7 +55,8 @@ func (s SLO) Validate() error { return sloValidation.Validate(s) } -// String returns the qualified object name. +// String returns the SLO's formatted version and kind. It also returns +// [Metadata.Name] when set. func (s SLO) String() string { return internal.GetObjectName(s) } @@ -72,18 +74,21 @@ func (s SLO) GetValidator() govy.Validator[SLO] { // SLOSpec defines the service, indicator, objectives, time window, and // error-budget calculation for an [SLO]. type SLOSpec struct { - // TimeWindows contains the SLO evaluation window. + // TimeWindows contains exactly one SLO evaluation window. TimeWindows []SLOTimeWindow `json:"timeWindows"` // BudgetingMethod applies the selected error-budget calculation to every // objective. BudgetingMethod SLOBudgetingMethod `json:"budgetingMethod"` - // Description summarizes the SLO. + // Description is an optional summary of the SLO. Description string `json:"description,omitempty"` - // Indicator defines the threshold-metric form of the SLO. + // Indicator defines the threshold-metric form of the SLO. It must be nil for + // the ratio form. Indicator *SLOIndicator `json:"indicator"` - // Service identifies the service whose reliability the SLO measures. + // Service is the metadata name of the [Service] whose reliability the SLO + // measures. Service string `json:"service"` - // Objectives contains the targets used to evaluate the indicator. + // Objectives contains reliability targets. For the ratio form, each + // objective's [SLOObjective.RatioMetrics] defines the SLI metric queries. Objectives []SLOObjective `json:"objectives"` } @@ -151,7 +156,9 @@ type SLORatioMetrics struct { } // SLOTimeWindow defines the period over which an SLO is evaluated. For example, -// a Unit of Week and a Count of 4 define a four-week window. +// a Unit of Week and a Count of 4 define a four-week window. A rolling window +// requires IsRolling to be true and Calendar to be nil. A calendar-aligned +// window requires IsRolling to be false and Calendar to be non-nil. type SLOTimeWindow struct { // Unit combines with Count to set the window length. Unit SLOTimeWindowUnit `json:"unit"` @@ -160,7 +167,8 @@ type SLOTimeWindow struct { // IsRolling selects a continuously advancing window when true and a // calendar-aligned window when false. IsRolling bool `json:"isRolling"` - // Calendar defines the alignment of a calendar window. + // Calendar defines the alignment when IsRolling is false. It must be nil + // when IsRolling is true. Calendar *SLOCalendar `json:"calendar,omitempty"` } @@ -246,6 +254,7 @@ var sloSpecValidation = govy.New( Include(sloTimeSlicesObjectiveValidation), govy.For(func(spec SLOSpec) string { return spec.Description }). WithName("description"). + OmitEmpty(). Rules(rules.StringMaxLength(1050)), govy.For(func(spec SLOSpec) string { return spec.Service }). WithName("service"). @@ -283,7 +292,9 @@ var sloTimeWindowValidation = govy.New( return govy.NewRuleError("'calendar' must be set when 'isRolling' is false") } return nil - })), + }).WithDescription( + "'calendar' must be set when 'isRolling' is false and cannot be set when 'isRolling' is true", + )), govy.For(func(t SLOTimeWindow) SLOTimeWindowUnit { return t.Unit }). WithName("unit"). Required(). diff --git a/pkg/openslo/v2alpha/alert_condition.go b/pkg/openslo/v2alpha/alert_condition.go index 221d068..9539623 100644 --- a/pkg/openslo/v2alpha/alert_condition.go +++ b/pkg/openslo/v2alpha/alert_condition.go @@ -23,7 +23,14 @@ func NewAlertCondition(metadata Metadata, spec AlertConditionSpec) AlertConditio } } -// AlertCondition defines when an SLO alert is considered breaching. +// AlertCondition defines when an SLO alert condition is breaching. +// [AlertPolicySpec.AlertWhenBreaching] controls whether that state triggers an +// alert. +// +// This type is the SDK's v2alpha representation. The living, unstable +// [OpenSLO v2alpha proposal] does not define alert objects. +// +// [OpenSLO v2alpha proposal]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md type AlertCondition struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -51,7 +58,8 @@ func (a AlertCondition) Validate() error { return alertConditionValidation.Validate(a) } -// String returns the alert condition's formatted version, kind, and name. +// String returns the alert condition's formatted version and kind. +// It also returns the metadata name when set. func (a AlertCondition) String() string { return internal.GetObjectName(a) } @@ -71,7 +79,7 @@ type AlertConditionSpec struct { // Severity is a consumer-defined alert classification. Severity string `json:"severity"` Condition AlertConditionType `json:"condition"` - // Description summarizes the alert condition. + // Description optionally summarizes the alert condition in at most 1,050 characters. Description string `json:"description,omitempty"` } @@ -79,8 +87,8 @@ type AlertConditionSpec struct { // Burn rate is error-budget consumption relative to the rate allowed by the // SLO. type AlertConditionType struct { - // Kind selects the condition algorithm. OpenSLO defaults omitted values to - // [AlertConditionKindBurnRate]. This SDK does not apply that default. + // Kind selects the condition algorithm. This SDK requires Kind and does not + // apply the OpenSLO v1 default of [AlertConditionKindBurnRate]. Kind AlertConditionKind `json:"kind"` // Operator compares the calculated burn rate with Threshold. Operator Operator `json:"op"` @@ -88,9 +96,9 @@ type AlertConditionType struct { Threshold *float64 `json:"threshold"` // LookbackWindow sets the period for burn-rate calculation. LookbackWindow DurationShorthand `json:"lookbackWindow"` - // AlertAfter sets how long the condition must remain valid before an alert is - // triggered. OpenSLO defaults omitted values to "0m". This SDK does not apply - // that default. + // AlertAfter sets how long the burn-rate comparison must remain true before + // the condition becomes breaching. This SDK requires AlertAfter and does not + // apply the OpenSLO v1 default of "0m". AlertAfter DurationShorthand `json:"alertAfter"` } @@ -99,6 +107,7 @@ type AlertConditionType struct { type AlertConditionKind string const ( + // AlertConditionKindBurnRate selects an error-budget burn-rate comparison. AlertConditionKindBurnRate AlertConditionKind = "burnrate" ) @@ -115,6 +124,7 @@ var alertConditionValidation = govy.New( var alertConditionSpecValidation = govy.New( govy.For(func(spec AlertConditionSpec) string { return spec.Description }). WithName("description"). + OmitEmpty(). Rules(rules.StringMaxLength(1050)), govy.For(func(spec AlertConditionSpec) string { return spec.Severity }). WithName("severity"). diff --git a/pkg/openslo/v2alpha/alert_notification_target.go b/pkg/openslo/v2alpha/alert_notification_target.go index b6b357b..df34895 100644 --- a/pkg/openslo/v2alpha/alert_notification_target.go +++ b/pkg/openslo/v2alpha/alert_notification_target.go @@ -23,7 +23,14 @@ func NewAlertNotificationTarget(metadata Metadata, spec AlertNotificationTargetS } } -// AlertNotificationTarget names a destination for alert delivery. +// AlertNotificationTarget represents a destination for alert delivery. +// The consuming implementation defines the format of +// [AlertNotificationTargetSpec.Target]. +// +// This type is the SDK's v2alpha representation. The living, unstable +// [OpenSLO v2alpha proposal] does not define alert objects. +// +// [OpenSLO v2alpha proposal]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md type AlertNotificationTarget struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -51,7 +58,8 @@ func (a AlertNotificationTarget) Validate() error { return alertNotificationTargetValidation.Validate(a) } -// String returns the notification target's formatted version, kind, and name. +// String returns the notification target's formatted version and kind. +// It also returns the metadata name when set. func (a AlertNotificationTarget) String() string { return internal.GetObjectName(a) } @@ -66,12 +74,15 @@ func (a AlertNotificationTarget) GetValidator() govy.Validator[AlertNotification return alertNotificationTargetValidation } -// AlertNotificationTargetSpec identifies the consumer-defined notification -// destination. +// AlertNotificationTargetSpec identifies a notification destination. +// The consuming implementation defines the required +// [AlertNotificationTargetSpec.Target] format. type AlertNotificationTargetSpec struct { - // Description summarizes the destination. + // Description optionally summarizes the target in at most 1,050 characters. Description string `json:"description,omitempty"` - // Target identifies the consumer-defined destination for alert delivery. + // Target specifies the notification destination in the format that the + // consuming implementation requires. Examples include "email", "slack", + // "web-hook", and "Opsgenie". Target string `json:"target"` } @@ -96,5 +107,6 @@ var alertNotificationTargetSpecValidation = govy.New( Required(), govy.For(func(spec AlertNotificationTargetSpec) string { return spec.Description }). WithName("description"). + OmitEmpty(). Rules(rules.StringMaxLength(1050)), ) diff --git a/pkg/openslo/v2alpha/alert_policy.go b/pkg/openslo/v2alpha/alert_policy.go index 494ba70..8112df7 100644 --- a/pkg/openslo/v2alpha/alert_policy.go +++ b/pkg/openslo/v2alpha/alert_policy.go @@ -23,7 +23,13 @@ func NewAlertPolicy(metadata Metadata, spec AlertPolicySpec) AlertPolicy { } } -// AlertPolicy defines when the system emits an SLO alert and where it sends it. +// AlertPolicy defines which alert-condition states trigger an SLO alert. +// It also defines the notification destinations for triggered alerts. +// +// This type is the SDK's v2alpha representation. The living, unstable +// [OpenSLO v2alpha proposal] does not define alert objects. +// +// [OpenSLO v2alpha proposal]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md type AlertPolicy struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -51,7 +57,8 @@ func (a AlertPolicy) Validate() error { return alertPolicyValidation.Validate(a) } -// String returns the alert policy's formatted version, kind, and name. +// String returns the alert policy's formatted version and kind. +// It also returns the metadata name when set. func (a AlertPolicy) String() string { return internal.GetObjectName(a) } @@ -68,24 +75,29 @@ func (a AlertPolicy) GetValidator() govy.Validator[AlertPolicy] { // AlertPolicySpec defines the trigger states, condition, and notification // destinations for an [AlertPolicy]. +// The trigger flags are independent and have a false zero value. +// JSON encoding omits false values. This SDK applies no omission default and +// accepts all three flags as false. type AlertPolicySpec struct { - // Description summarizes the alert policy. + // Description optionally summarizes the alert policy in at most 1,050 characters. Description string `json:"description,omitempty"` - // AlertWhenNoData enables notifications when the associated SLO has no - // burn-rate value. + // AlertWhenNoData controls whether a missing burn-rate value triggers an + // alert. AlertWhenNoData bool `json:"alertWhenNoData,omitempty"` - // AlertWhenBreaching enables notifications when the condition is breaching. + // AlertWhenBreaching controls whether a breaching condition triggers an alert. AlertWhenBreaching bool `json:"alertWhenBreaching,omitempty"` - // AlertWhenResolved enables notifications when the condition resolves. + // AlertWhenResolved controls whether a resolved condition triggers an alert. AlertWhenResolved bool `json:"alertWhenResolved,omitempty"` - // Conditions supplies the policy's alert condition by reference or inline. + // Conditions contains exactly one alert condition, specified inline or by + // reference. Conditions []AlertPolicyCondition `json:"conditions,omitempty"` - // NotificationTargets lists referenced or inline delivery destinations. + // NotificationTargets contains one or more delivery destinations. + // Specify each destination inline or by reference. NotificationTargets []AlertPolicyNotificationTarget `json:"notificationTargets,omitempty"` } -// AlertPolicyCondition supplies a condition to an [AlertPolicy] by reference -// or inline definition. +// AlertPolicyCondition supplies exactly one condition to an [AlertPolicy]. +// Set either the reference or the inline definition. type AlertPolicyCondition struct { *AlertPolicyConditionRef *AlertPolicyConditionInline @@ -93,6 +105,7 @@ type AlertPolicyCondition struct { // AlertPolicyConditionInline is an alert-condition definition embedded in an // [AlertPolicy]. +// The inline form contains kind, metadata, and spec, but no API version. type AlertPolicyConditionInline struct { Kind openslo.Kind `json:"kind"` Metadata Metadata `json:"metadata"` @@ -101,12 +114,12 @@ type AlertPolicyConditionInline struct { // AlertPolicyConditionRef identifies a separately defined [AlertCondition]. type AlertPolicyConditionRef struct { - // ConditionRef names the alert condition to use. + // ConditionRef is the metadata name of the alert condition to use. ConditionRef string `json:"conditionRef"` } -// AlertPolicyNotificationTarget supplies a notification destination to an -// [AlertPolicy] by reference or inline definition. +// AlertPolicyNotificationTarget supplies exactly one notification destination +// to an [AlertPolicy]. Set either the reference or the inline definition. type AlertPolicyNotificationTarget struct { *AlertPolicyNotificationTargetRef *AlertPolicyNotificationTargetInline @@ -114,6 +127,7 @@ type AlertPolicyNotificationTarget struct { // AlertPolicyNotificationTargetInline is an alert-notification-target // definition embedded in an [AlertPolicy]. +// The inline form contains kind, metadata, and spec, but no API version. type AlertPolicyNotificationTargetInline struct { Kind openslo.Kind `json:"kind"` Metadata Metadata `json:"metadata"` @@ -123,7 +137,7 @@ type AlertPolicyNotificationTargetInline struct { // AlertPolicyNotificationTargetRef identifies a separately defined // [AlertNotificationTarget]. type AlertPolicyNotificationTargetRef struct { - // TargetRef names the notification target to use. + // TargetRef is the metadata name of the notification target to use. TargetRef string `json:"targetRef"` } @@ -139,7 +153,17 @@ var alertPolicyValidation = govy.New( var alertPolicySpecValidation = govy.New( govy.For(func(spec AlertPolicySpec) string { return spec.Description }). WithName("description"). + OmitEmpty(). Rules(rules.StringMaxLength(1050)), + govy.For(func(spec AlertPolicySpec) bool { return spec.AlertWhenNoData }). + WithName("alertWhenNoData"). + OmitEmpty(), + govy.For(func(spec AlertPolicySpec) bool { return spec.AlertWhenBreaching }). + WithName("alertWhenBreaching"). + OmitEmpty(), + govy.For(func(spec AlertPolicySpec) bool { return spec.AlertWhenResolved }). + WithName("alertWhenResolved"). + OmitEmpty(), govy.ForSlice(func(spec AlertPolicySpec) []AlertPolicyCondition { return spec.Conditions }). WithName("conditions"). Rules(rules.SliceLength[[]AlertPolicyCondition](1, 1)). diff --git a/pkg/openslo/v2alpha/data_source.go b/pkg/openslo/v2alpha/data_source.go index 730524b..003890c 100644 --- a/pkg/openslo/v2alpha/data_source.go +++ b/pkg/openslo/v2alpha/data_source.go @@ -25,7 +25,15 @@ func NewDataSource(metadata Metadata, spec DataSourceSpec) DataSource { } } -// DataSource makes metric-source connection details reusable across SLIs. +// DataSource represents reusable connection details for a metric source. +// [SLIMetricSpec.DataSourceRef] selects it by metadata name. +// A metric query can instead embed [SLIMetricSpec.DataSourceSpec]. +// [SLIMetricSpec.Spec] contains implementation-defined query configuration. +// +// This type is the SDK's v2alpha representation. The living, unstable +// [OpenSLO v2alpha proposal] does not define a standalone DataSource schema. +// +// [OpenSLO v2alpha proposal]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md type DataSource struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -53,7 +61,8 @@ func (d DataSource) Validate() error { return dataSourceValidation.Validate(d) } -// String returns the data source's formatted version, kind, and name. +// String returns the data source's formatted version and kind. +// It also returns the metadata name when set. func (d DataSource) String() string { return internal.GetObjectName(d) } @@ -68,14 +77,16 @@ func (d DataSource) GetValidator() govy.Validator[DataSource] { return dataSourceValidation } -// DataSourceSpec defines a data-source type and its provider-specific connection -// configuration. +// DataSourceSpec defines a metric-source type and its implementation-defined +// connection data. type DataSourceSpec struct { - // Description summarizes the data source. + // Description optionally summarizes the data source in at most 1,050 characters. Description string `json:"description,omitempty"` - // Type identifies the data-source implementation understood by the consumer. + // Type identifies the metric-source type, such as Prometheus or Datadog. + // The inherited OpenSLO v1 model does not standardize the accepted Type values. Type string `json:"type"` - // ConnectionDetails stores provider-specific connection configuration as JSON. + // ConnectionDetails contains implementation-defined connection data encoded + // as JSON, such as endpoints or authentication settings. ConnectionDetails json.RawMessage `json:"connectionDetails"` } @@ -92,6 +103,7 @@ var dataSourceValidation = govy.New( var dataSourceSpecValidation = govy.New( govy.For(func(spec DataSourceSpec) string { return spec.Description }). WithName("description"). + OmitEmpty(). Rules(rules.StringMaxLength(1050)), govy.For(func(spec DataSourceSpec) string { return spec.Type }). WithName("type"). diff --git a/pkg/openslo/v2alpha/objects.go b/pkg/openslo/v2alpha/objects.go index fde5730..9b7c932 100644 --- a/pkg/openslo/v2alpha/objects.go +++ b/pkg/openslo/v2alpha/objects.go @@ -41,9 +41,11 @@ type Object interface { type Metadata struct { // Name identifies the object when other OpenSLO objects refer to it. Name string `json:"name"` - // Labels classifies the object with Kubernetes-style, single-valued labels. + // Labels optionally classifies the object with Kubernetes-style, + // single-valued labels. Labels Labels `json:"labels,omitempty"` - // Annotations attaches non-identifying metadata with qualified keys. + // Annotations optionally attaches non-identifying metadata with qualified + // keys. Annotations Annotations `json:"annotations,omitempty"` } @@ -110,9 +112,11 @@ func validationRulesMetadata[T any](getter func(T) Metadata) govy.PropertyRules[ Rules(rules.StringDNSLabel()), govy.For(func(m Metadata) Labels { return m.Labels }). WithName("labels"). + OmitEmpty(). Include(labelsValidator()), govy.For(func(m Metadata) Annotations { return m.Annotations }). WithName("annotations"). + OmitEmpty(). Include(annotationsValidator()), ), ) diff --git a/pkg/openslo/v2alpha/service.go b/pkg/openslo/v2alpha/service.go index 850d87d..9009335 100644 --- a/pkg/openslo/v2alpha/service.go +++ b/pkg/openslo/v2alpha/service.go @@ -23,7 +23,15 @@ func NewService(metadata Metadata, spec ServiceSpec) Service { } } -// Service groups SLOs under a named service. +// Service identifies a high-level group for SLOs. +// Each [SLO] refers to a Service by metadata name through [SLOSpec.ServiceRef]. +// Multiple SLOs can use the same Service name. +// The SDK does not verify that a referenced Service exists. +// +// This type is the SDK's v2alpha representation. The living, unstable +// [OpenSLO v2alpha proposal] does not define a standalone Service schema. +// +// [OpenSLO v2alpha proposal]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md type Service struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -51,7 +59,8 @@ func (s Service) Validate() error { return serviceValidation.Validate(s) } -// String returns the service's formatted version, kind, and name. +// String returns the service's formatted version and kind. +// It also returns the metadata name when set. func (s Service) String() string { return internal.GetObjectName(s) } @@ -68,7 +77,7 @@ func (s Service) GetValidator() govy.Validator[Service] { // ServiceSpec defines the descriptive attributes of a [Service]. type ServiceSpec struct { - // Description summarizes the service. + // Description optionally summarizes the service in at most 1,050 characters. Description string `json:"description,omitempty"` } @@ -81,6 +90,7 @@ var serviceValidation = govy.New( Include(govy.New( govy.For(func(spec ServiceSpec) string { return spec.Description }). WithName("description"). + OmitEmpty(). Rules(rules.StringMaxLength(1050)), )), ).WithNameFunc(internal.GetObjectName[Service]) diff --git a/pkg/openslo/v2alpha/sli.go b/pkg/openslo/v2alpha/sli.go index a4cd1fe..067863f 100644 --- a/pkg/openslo/v2alpha/sli.go +++ b/pkg/openslo/v2alpha/sli.go @@ -23,8 +23,8 @@ func NewSLI(metadata Metadata, spec SLISpec) SLI { } } -// SLI describes how to read a metric from a data source, as defined by the -// [OpenSLO v2alpha SLI]. +// SLI defines a derived reliability indicator calculated from one or more +// metric queries against data sources, as defined by the [OpenSLO v2alpha SLI]. // // [OpenSLO v2alpha SLI]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md#sli type SLI struct { @@ -54,7 +54,8 @@ func (s SLI) Validate() error { return sliValidation.Validate(s) } -// String returns the SLI's formatted version, kind, and name. +// String returns the SLI's formatted version and kind. +// It also returns the metadata name when set. func (s SLI) String() string { return internal.GetObjectName(s) } @@ -69,19 +70,22 @@ func (s SLI) GetValidator() govy.Validator[SLI] { return sliValidation } -// SLISpec defines the metric used by an [SLI]. +// SLISpec defines the query or queries used to calculate an [SLI]. type SLISpec struct { - // Description summarizes the indicator. + // Description optionally summarizes the indicator in at most 1,050 characters. Description string `json:"description,omitempty"` - // ThresholdMetric supplies values for [SLOObjective.Operator] to compare with - // [SLOObjective.Value]. + // ThresholdMetric defines a query that returns values for comparison with + // [SLOObjective.Value] by [SLOObjective.Operator]. ThresholdMetric *SLIMetricSpec `json:"thresholdMetric,omitempty"` - // RatioMetric supplies a success ratio for an SLO objective. + // RatioMetric defines component queries or a precomputed ratio for an SLO + // objective. RatioMetric *SLIRatioMetric `json:"ratioMetric,omitempty"` } -// SLIRatioMetric derives a success ratio as good divided by total, total minus -// bad divided by total, or a precomputed raw ratio. +// SLIRatioMetric defines an indicator as [SLIRatioMetric.Good] divided by +// [SLIRatioMetric.Total], ([SLIRatioMetric.Total] minus [SLIRatioMetric.Bad]) +// divided by [SLIRatioMetric.Total], or [SLIRatioMetric.Raw]. +// [SLIRatioMetric.RawType] identifies Raw as a success or failure ratio. // For example, 990 good events out of 1,000 total events produce 0.99. // 10 bad events with the same total produce the same success ratio. type SLIRatioMetric struct { @@ -115,15 +119,15 @@ var validSLIRawMetricTypes = []SLIRawMetricType{ SLIRawMetricTypeFailure, } -// SLIMetricSpec supplies a provider-specific query in the v2alpha flattened -// layout. +// SLIMetricSpec supplies an implementation-defined query in the v2alpha +// flattened layout. type SLIMetricSpec struct { // DataSourceRef names an existing [DataSource]. DataSourceRef string `json:"dataSourceRef,omitempty"` - // DataSourceSpec embeds the data-source configuration. + // DataSourceSpec embeds the complete data-source connection configuration. DataSourceSpec *DataSourceSpec `json:"dataSourceSpec,omitempty"` - // Spec contains provider-specific query configuration at the same level as - // the data-source selection. + // Spec contains implementation-defined query configuration at the same level + // as the data-source selection. Spec map[string]any `json:"spec,omitempty"` } @@ -139,6 +143,7 @@ var sliValidation = govy.New( var sliSpecValidation = govy.New( govy.For(func(spec SLISpec) string { return spec.Description }). WithName("description"). + OmitEmpty(). Rules(rules.StringMaxLength(1050)), govy.For(govy.GetSelf[SLISpec]()). Rules(rules.MutuallyExclusive(true, map[string]func(s SLISpec) any{ @@ -176,7 +181,11 @@ var sliFractionMetricValidation = govy.New( Rules(rules.OneOfProperties(map[string]func(m SLIRatioMetric) any{ "good": func(m SLIRatioMetric) any { return m.Good }, "bad": func(m SLIRatioMetric) any { return m.Bad }, - })), + })). + When( + func(m SLIRatioMetric) bool { return m.Total != nil }, + govy.WhenDescription("'total' is set"), + ), govy.ForPointer(func(m SLIRatioMetric) *SLIMetricSpec { return m.Total }). WithName("total"). Cascade(govy.CascadeModeContinue). @@ -184,25 +193,12 @@ var sliFractionMetricValidation = govy.New( govy.ForPointer(func(m SLIRatioMetric) *SLIMetricSpec { return m.Good }). WithName("good"). Cascade(govy.CascadeModeContinue). - When( - func(m SLIRatioMetric) bool { return m.Good != nil }, - govy.WhenDescription("'good' is set"), - ). Include(sliMetricSpecValidation), govy.ForPointer(func(m SLIRatioMetric) *SLIMetricSpec { return m.Bad }). WithName("bad"). Cascade(govy.CascadeModeContinue). - When( - func(m SLIRatioMetric) bool { return m.Bad != nil }, - govy.WhenDescription("'bad' is set"), - ). Include(sliMetricSpecValidation), -). - Cascade(govy.CascadeModeStop). - When( - func(m SLIRatioMetric) bool { return m.Total != nil }, - govy.WhenDescription("'total' is set"), - ) +).Cascade(govy.CascadeModeStop) var sliRawMetricSpecValidation = govy.New( govy.ForPointer(func(m SLIRatioMetric) *SLIMetricSpec { return m.Raw }). @@ -211,12 +207,12 @@ var sliRawMetricSpecValidation = govy.New( govy.For(func(m SLIRatioMetric) SLIRawMetricType { return m.RawType }). WithName("rawType"). Required(). - Rules(rules.OneOf(validSLIRawMetricTypes...)), -). - When( - func(m SLIRatioMetric) bool { return m.Raw != nil }, - govy.WhenDescription("'raw' is set"), - ) + Rules(rules.OneOf(validSLIRawMetricTypes...)). + When( + func(m SLIRatioMetric) bool { return m.Raw != nil }, + govy.WhenDescription("'raw' is set"), + ), +) var sliMetricSpecValidation = govy.New( govy.For(govy.GetSelf[SLIMetricSpec]()). diff --git a/pkg/openslo/v2alpha/slo.go b/pkg/openslo/v2alpha/slo.go index a590a23..438c781 100644 --- a/pkg/openslo/v2alpha/slo.go +++ b/pkg/openslo/v2alpha/slo.go @@ -57,7 +57,8 @@ func (s SLO) Validate() error { return sloValidation.Validate(s) } -// String returns the SLO's formatted version, kind, and name. +// String returns the SLO's formatted version and kind. +// It also returns the metadata name when set. func (s SLO) String() string { return internal.GetObjectName(s) } @@ -81,7 +82,7 @@ func (s SLO) GetValidator() govy.Validator[SLO] { // objectives, and alert policies. A standard SLO applies one SLI to all // objectives. A composite SLO can select a different SLI per objective. type SLOSpec struct { - // Description summarizes the SLO. + // Description optionally summarizes the SLO in at most 1,050 characters. Description string `json:"description,omitempty"` // ServiceRef names the service associated with this SLO. The SDK serializes // the field as "serviceRef". The living v2alpha proposal calls it "service". @@ -93,12 +94,16 @@ type SLOSpec struct { // BudgetingMethod applies the selected error-budget calculation to every // objective. BudgetingMethod SLOBudgetingMethod `json:"budgetingMethod"` - // TimeWindow defines the SLO evaluation period and its alignment. + // TimeWindow contains exactly one SLO evaluation window. TimeWindow []SLOTimeWindow `json:"timeWindow,omitempty"` // Objectives contains the SLO's budget targets and metric thresholds. // V2alpha permits multiple objectives for a threshold-metric SLO. + // OpenSLO v1 requires Objectives. The living v2alpha proposal does not state a + // requiredness change. This SDK accepts an omitted Objectives field. Objectives []SLOObjective `json:"objectives"` - // AlertPolicies contains inline or referenced policies associated with the SLO. + // AlertPolicies contains policies associated with the SLO. + // Each item must specify exactly one inline definition or metadata-name + // reference. AlertPolicies []SLOAlertPolicy `json:"alertPolicies,omitempty"` } @@ -138,9 +143,14 @@ type SLOSLIInline struct { } // SLOObjective defines one error-budget target and, for a threshold SLI, its -// metric comparison. The SDK validates threshold-specific fields only for an -// embedded SLI. A referenced SLI does not expose its metric type during -// validation. +// metric comparison. The living v2alpha proposal also defines objective labels, +// which this SDK does not model. +// +// For a standard SLO with an inline threshold SLI, validation requires +// [SLOObjective.Operator] and [SLOObjective.Value]. For a standard SLO with an +// inline ratio SLI, validation forbids them. The SDK does not apply these +// metric-type rules to referenced SLIs or to SLIs embedded in composite +// objectives. type SLOObjective struct { // DisplayName is a human-readable name for this objective. // It is not part of the enclosing object's [Metadata]. @@ -164,13 +174,16 @@ type SLOObjective struct { SLI *SLOSLIInline `json:"sli,omitempty"` // SLIRef names this objective's existing [SLI] for a composite SLO. SLIRef *string `json:"sliRef,omitempty"` - // CompositeWeight scales this objective's contribution to a multi-objective - // composite SLO. OpenSLO uses a weight of 1 when omitted. This SDK leaves the - // field unset. + // CompositeWeight scales this objective's contribution to a composite SLO. + // The living v2alpha proposal permits it only with multiple objectives and + // defaults it to 1. This SDK does not enforce the objective-count restriction + // and preserves an omitted value as nil. CompositeWeight *float64 `json:"compositeWeight,omitempty"` } -// SLOTimeWindow describes one rolling or calendar-aligned SLO evaluation window. +// SLOTimeWindow describes one rolling or calendar-aligned evaluation window. +// If [SLOTimeWindow.IsRolling] is true, [SLOTimeWindow.Calendar] must be nil. +// If IsRolling is false, Calendar must be non-nil. type SLOTimeWindow struct { // Duration is the length of the evaluation window. Duration DurationShorthand `json:"duration"` @@ -190,13 +203,15 @@ type SLOCalendar struct { TimeZone string `json:"timeZone"` } -// SLOAlertPolicy associates an alert policy with an [SLO]. +// SLOAlertPolicy associates exactly one inline or referenced alert policy with +// an [SLO]. type SLOAlertPolicy struct { *SLOAlertPolicyInline *SLOAlertPolicyRef } // SLOAlertPolicyInline is an alert-policy definition embedded in an SLO. +// The inline form contains kind, metadata, and spec, but no API version. type SLOAlertPolicyInline struct { Kind openslo.Kind `json:"kind"` Metadata Metadata `json:"metadata"` @@ -205,7 +220,7 @@ type SLOAlertPolicyInline struct { // SLOAlertPolicyRef identifies a separately defined [AlertPolicy]. type SLOAlertPolicyRef struct { - // AlertPolicyRef names the alert policy to use. + // AlertPolicyRef is the metadata name of the alert policy to use. AlertPolicyRef string `json:"alertPolicyRef"` } @@ -232,6 +247,7 @@ var sloSpecValidation = govy.New( ), govy.For(func(spec SLOSpec) string { return spec.Description }). WithName("description"). + OmitEmpty(). Rules(rules.StringMaxLength(1050)), govy.For(func(spec SLOSpec) string { return spec.ServiceRef }). WithName("serviceRef"). @@ -311,7 +327,9 @@ var sloTimeWindowValidation = govy.New( return govy.NewRuleError("'calendar' must be set when 'isRolling' is false") } return nil - })), + }).WithDescription( + "'calendar' must be set when 'isRolling' is false and cannot be set when 'isRolling' is true", + )), govy.For(func(t SLOTimeWindow) DurationShorthand { return t.Duration }). WithName("duration"). Required(). From 121cb1f8377a0b6dfdc071d867d7e88886ed7ca5 Mon Sep 17 00:00:00 2001 From: Mateusz Hawrus Date: Wed, 19 Aug 2026 18:05:12 +0200 Subject: [PATCH 5/6] Clarify v2alpha documentation and resolve promoted field docs Remove outdated proposal references and align generated documentation with current SDK behavior. Update normalization tests to resolve field documentation by owning type and field name. --- docs/manifest.json | 88 +++++++++---------- internal/cmd/objectdoc/main_test.go | 77 ++++++++++------ pkg/openslo/v2alpha/alert_condition.go | 11 +-- .../v2alpha/alert_notification_target.go | 5 -- pkg/openslo/v2alpha/alert_policy.go | 5 -- pkg/openslo/v2alpha/data_source.go | 7 +- pkg/openslo/v2alpha/doc.go | 12 +-- pkg/openslo/v2alpha/objects.go | 2 +- pkg/openslo/v2alpha/service.go | 5 -- pkg/openslo/v2alpha/sli.go | 4 +- pkg/openslo/v2alpha/slo.go | 8 +- 11 files changed, 103 insertions(+), 121 deletions(-) diff --git a/docs/manifest.json b/docs/manifest.json index c20f475..3249c52 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -10,7 +10,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "typeDoc": "AlertCondition defines when an SLO alert condition is breaching. [AlertPolicySpec.AlertWhenBreaching](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicySpec.AlertWhenBreaching) controls whether that state triggers an alert.\n\nThis type is the SDK's v2alpha representation. The living, unstable [OpenSLO v2alpha proposal](https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md) does not define alert objects.", + "typeDoc": "AlertCondition defines when an SLO alert condition is breaching. [AlertPolicySpec.AlertWhenBreaching](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertPolicySpec.AlertWhenBreaching) controls whether that state triggers an alert.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -75,7 +75,7 @@ "errorCode": "required" } ], - "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. It contains a name and optional labels and annotations.", "childrenPaths": [ "$.metadata.name", "$.metadata.labels", @@ -298,7 +298,7 @@ } ], "typeDoc": "AlertConditionKind identifies the evaluation algorithm for an [AlertConditionType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionType).", - "fieldDoc": "Kind selects the condition algorithm. This SDK requires Kind and does not apply the OpenSLO v1 default of [AlertConditionKindBurnRate](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionKindBurnRate)." + "fieldDoc": "Kind selects the condition algorithm. This SDK requires Kind." }, { "path": "$.spec.condition.op", @@ -385,7 +385,7 @@ } ], "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", - "fieldDoc": "AlertAfter sets how long the burn-rate comparison must remain true before the condition becomes breaching. This SDK requires AlertAfter and does not apply the OpenSLO v1 default of \"0m\"." + "fieldDoc": "AlertAfter sets how long the burn-rate comparison must remain true before the condition becomes breaching. This SDK requires AlertAfter." }, { "path": "$.spec.description", @@ -417,7 +417,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "typeDoc": "AlertNotificationTarget represents a destination for alert delivery. The consuming implementation defines the format of [AlertNotificationTargetSpec.Target](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertNotificationTargetSpec.Target).\n\nThis type is the SDK's v2alpha representation. The living, unstable [OpenSLO v2alpha proposal](https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md) does not define alert objects.", + "typeDoc": "AlertNotificationTarget represents a destination for alert delivery. The consuming implementation defines the format of [AlertNotificationTargetSpec.Target](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertNotificationTargetSpec.Target).", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -482,7 +482,7 @@ "errorCode": "required" } ], - "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. It contains a name and optional labels and annotations.", "childrenPaths": [ "$.metadata.name", "$.metadata.labels", @@ -691,7 +691,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "typeDoc": "AlertPolicy defines which alert-condition states trigger an SLO alert. It also defines the notification destinations for triggered alerts.\n\nThis type is the SDK's v2alpha representation. The living, unstable [OpenSLO v2alpha proposal](https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md) does not define alert objects.", + "typeDoc": "AlertPolicy defines which alert-condition states trigger an SLO alert. It also defines the notification destinations for triggered alerts.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -756,7 +756,7 @@ "errorCode": "required" } ], - "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. It contains a name and optional labels and annotations.", "childrenPaths": [ "$.metadata.name", "$.metadata.labels", @@ -1083,7 +1083,7 @@ "errorCode": "required" } ], - "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. It contains a name and optional labels and annotations.", "childrenPaths": [ "$.spec.conditions[*].metadata.name", "$.spec.conditions[*].metadata.labels", @@ -1306,7 +1306,7 @@ } ], "typeDoc": "AlertConditionKind identifies the evaluation algorithm for an [AlertConditionType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionType).", - "fieldDoc": "Kind selects the condition algorithm. This SDK requires Kind and does not apply the OpenSLO v1 default of [AlertConditionKindBurnRate](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionKindBurnRate)." + "fieldDoc": "Kind selects the condition algorithm. This SDK requires Kind." }, { "path": "$.spec.conditions[*].spec.condition.op", @@ -1393,7 +1393,7 @@ } ], "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", - "fieldDoc": "AlertAfter sets how long the burn-rate comparison must remain true before the condition becomes breaching. This SDK requires AlertAfter and does not apply the OpenSLO v1 default of \"0m\"." + "fieldDoc": "AlertAfter sets how long the burn-rate comparison must remain true before the condition becomes breaching. This SDK requires AlertAfter." }, { "path": "$.spec.conditions[*].spec.description", @@ -1515,7 +1515,7 @@ "errorCode": "required" } ], - "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. It contains a name and optional labels and annotations.", "childrenPaths": [ "$.spec.notificationTargets[*].metadata.name", "$.spec.notificationTargets[*].metadata.labels", @@ -1724,7 +1724,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "typeDoc": "DataSource represents reusable connection details for a metric source. [SLIMetricSpec.DataSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIMetricSpec.DataSourceRef) selects it by metadata name. A metric query can instead embed [SLIMetricSpec.DataSourceSpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIMetricSpec.DataSourceSpec). [SLIMetricSpec.Spec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIMetricSpec.Spec) contains implementation-defined query configuration.\n\nThis type is the SDK's v2alpha representation. The living, unstable [OpenSLO v2alpha proposal](https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md) does not define a standalone DataSource schema.", + "typeDoc": "DataSource represents reusable connection details for a metric source. [SLIMetricSpec.DataSourceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIMetricSpec.DataSourceRef) selects it by metadata name. A metric query can instead embed [SLIMetricSpec.DataSourceSpec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIMetricSpec.DataSourceSpec). [SLIMetricSpec.Spec](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLIMetricSpec.Spec) contains implementation-defined query configuration.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -1789,7 +1789,7 @@ "errorCode": "required" } ], - "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. It contains a name and optional labels and annotations.", "childrenPaths": [ "$.metadata.name", "$.metadata.labels", @@ -1985,7 +1985,7 @@ "errorCode": "required" } ], - "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The consuming implementation defines the accepted values." }, { "path": "$.spec.connectionDetails", @@ -2014,7 +2014,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "typeDoc": "SLI defines a derived reliability indicator calculated from one or more metric queries against data sources, as defined by the [OpenSLO v2alpha SLI](https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md#sli).", + "typeDoc": "SLI defines a derived reliability indicator calculated from one or more metric queries against data sources.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -2079,7 +2079,7 @@ "errorCode": "required" } ], - "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. It contains a name and optional labels and annotations.", "childrenPaths": [ "$.metadata.name", "$.metadata.labels", @@ -2366,7 +2366,7 @@ "errorCode": "required" } ], - "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The consuming implementation defines the accepted values." }, { "path": "$.spec.thresholdMetric.dataSourceSpec.connectionDetails", @@ -2559,7 +2559,7 @@ "errorCode": "required" } ], - "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The consuming implementation defines the accepted values." }, { "path": "$.spec.ratioMetric.good.dataSourceSpec.connectionDetails", @@ -2705,7 +2705,7 @@ "errorCode": "required" } ], - "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The consuming implementation defines the accepted values." }, { "path": "$.spec.ratioMetric.bad.dataSourceSpec.connectionDetails", @@ -2851,7 +2851,7 @@ "errorCode": "required" } ], - "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The consuming implementation defines the accepted values." }, { "path": "$.spec.ratioMetric.total.dataSourceSpec.connectionDetails", @@ -3027,7 +3027,7 @@ "errorCode": "required" } ], - "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The consuming implementation defines the accepted values." }, { "path": "$.spec.ratioMetric.raw.dataSourceSpec.connectionDetails", @@ -3082,7 +3082,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "typeDoc": "SLO defines a target for an SLI over a time window, as specified by the [OpenSLO v2alpha SLO](https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md#slo).", + "typeDoc": "SLO defines a target for an SLI over a time window.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -3147,7 +3147,7 @@ "errorCode": "required" } ], - "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. It contains a name and optional labels and annotations.", "childrenPaths": [ "$.metadata.name", "$.metadata.labels", @@ -3403,7 +3403,7 @@ ] } ], - "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. It contains a name and optional labels and annotations.", "childrenPaths": [ "$.spec.sli.metadata.name", "$.spec.sli.metadata.labels", @@ -3756,7 +3756,7 @@ ] } ], - "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The consuming implementation defines the accepted values." }, { "path": "$.spec.sli.spec.thresholdMetric.dataSourceSpec.connectionDetails", @@ -3989,7 +3989,7 @@ ] } ], - "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The consuming implementation defines the accepted values." }, { "path": "$.spec.sli.spec.ratioMetric.good.dataSourceSpec.connectionDetails", @@ -4165,7 +4165,7 @@ ] } ], - "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The consuming implementation defines the accepted values." }, { "path": "$.spec.sli.spec.ratioMetric.bad.dataSourceSpec.connectionDetails", @@ -4341,7 +4341,7 @@ ] } ], - "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The consuming implementation defines the accepted values." }, { "path": "$.spec.sli.spec.ratioMetric.total.dataSourceSpec.connectionDetails", @@ -4549,7 +4549,7 @@ ] } ], - "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The consuming implementation defines the accepted values." }, { "path": "$.spec.sli.spec.ratioMetric.raw.dataSourceSpec.connectionDetails", @@ -4775,7 +4775,7 @@ "kind": "[]struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "fieldDoc": "Objectives contains the SLO's budget targets and metric thresholds. V2alpha permits multiple objectives for a threshold-metric SLO. OpenSLO v1 requires Objectives. The living v2alpha proposal does not state a requiredness change. This SDK accepts an omitted Objectives field." + "fieldDoc": "Objectives contains the SLO's budget targets and metric thresholds. V2alpha permits multiple objectives for a threshold-metric SLO. This SDK accepts an omitted Objectives field." }, { "path": "$.spec.objectives[*]", @@ -5025,7 +5025,7 @@ ] } ], - "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. It contains a name and optional labels and annotations.", "childrenPaths": [ "$.spec.objectives[*].sli.metadata.name", "$.spec.objectives[*].sli.metadata.labels", @@ -5400,7 +5400,7 @@ ] } ], - "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The consuming implementation defines the accepted values." }, { "path": "$.spec.objectives[*].sli.spec.thresholdMetric.dataSourceSpec.connectionDetails", @@ -5647,7 +5647,7 @@ ] } ], - "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The consuming implementation defines the accepted values." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.good.dataSourceSpec.connectionDetails", @@ -5833,7 +5833,7 @@ ] } ], - "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The consuming implementation defines the accepted values." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.bad.dataSourceSpec.connectionDetails", @@ -6019,7 +6019,7 @@ ] } ], - "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The consuming implementation defines the accepted values." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.total.dataSourceSpec.connectionDetails", @@ -6239,7 +6239,7 @@ ] } ], - "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The inherited OpenSLO v1 model does not standardize the accepted Type values." + "fieldDoc": "Type identifies the metric-source type, such as Prometheus or Datadog. The consuming implementation defines the accepted values." }, { "path": "$.spec.objectives[*].sli.spec.ratioMetric.raw.dataSourceSpec.connectionDetails", @@ -6418,7 +6418,7 @@ "errorCode": "required" } ], - "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. It contains a name and optional labels and annotations.", "childrenPaths": [ "$.spec.alertPolicies[*].metadata.name", "$.spec.alertPolicies[*].metadata.labels", @@ -6751,7 +6751,7 @@ "errorCode": "required" } ], - "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. It contains a name and optional labels and annotations.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.conditions[*].metadata.name", "$.spec.alertPolicies[*].spec.conditions[*].metadata.labels", @@ -6974,7 +6974,7 @@ } ], "typeDoc": "AlertConditionKind identifies the evaluation algorithm for an [AlertConditionType](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionType).", - "fieldDoc": "Kind selects the condition algorithm. This SDK requires Kind and does not apply the OpenSLO v1 default of [AlertConditionKindBurnRate](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#AlertConditionKindBurnRate)." + "fieldDoc": "Kind selects the condition algorithm. This SDK requires Kind." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.condition.op", @@ -7061,7 +7061,7 @@ } ], "typeDoc": "DurationShorthand represents a duration as an integer with suffix \"m\", \"h\", \"d\", or \"w\". This SDK accepts zero and encodes it as empty text. OpenSLO specifies a positive integer.", - "fieldDoc": "AlertAfter sets how long the burn-rate comparison must remain true before the condition becomes breaching. This SDK requires AlertAfter and does not apply the OpenSLO v1 default of \"0m\"." + "fieldDoc": "AlertAfter sets how long the burn-rate comparison must remain true before the condition becomes breaching. This SDK requires AlertAfter." }, { "path": "$.spec.alertPolicies[*].spec.conditions[*].spec.description", @@ -7183,7 +7183,7 @@ "errorCode": "required" } ], - "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. It contains a name and optional labels and annotations.", "childrenPaths": [ "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.name", "$.spec.alertPolicies[*].spec.notificationTargets[*].metadata.labels", @@ -7419,7 +7419,7 @@ "kind": "struct", "package": "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" }, - "typeDoc": "Service identifies a high-level group for SLOs. Each [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLO) refers to a Service by metadata name through [SLOSpec.ServiceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOSpec.ServiceRef). Multiple SLOs can use the same Service name. The SDK does not verify that a referenced Service exists.\n\nThis type is the SDK's v2alpha representation. The living, unstable [OpenSLO v2alpha proposal](https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md) does not define a standalone Service schema.", + "typeDoc": "Service identifies a high-level group for SLOs. Each [SLO](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLO) refers to a Service by metadata name through [SLOSpec.ServiceRef](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha#SLOSpec.ServiceRef). Multiple SLOs can use the same Service name. The SDK does not verify that a referenced Service exists.", "childrenPaths": [ "$.apiVersion", "$.kind", @@ -7484,7 +7484,7 @@ "errorCode": "required" } ], - "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. Unlike v1 object metadata, it does not contain a display name.", + "typeDoc": "Metadata is the Kubernetes-style identifying metadata used by v2alpha objects. It contains a name and optional labels and annotations.", "childrenPaths": [ "$.metadata.name", "$.metadata.labels", diff --git a/internal/cmd/objectdoc/main_test.go b/internal/cmd/objectdoc/main_test.go index 5427773..34a94d4 100644 --- a/internal/cmd/objectdoc/main_test.go +++ b/internal/cmd/objectdoc/main_test.go @@ -259,52 +259,71 @@ func TestNormalizeGeneratedDocsRecoversPromotedFieldDocs(t *testing.T) { require.NoError(t, normalizeGeneratedDocs(docs)) tests := []struct { - name string - doc govydoc.ObjectDoc - path string - fieldDoc string + name string + doc govydoc.ObjectDoc + path string + owner reflect.Type + fieldName string }{ { - name: "v1 conditionRef", - doc: docs[0].doc, - path: "$.spec.conditions[*].conditionRef", - fieldDoc: "ConditionRef matches the [Metadata.Name](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#Metadata.Name) of an existing [AlertCondition](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertCondition).", + name: "v1 conditionRef", + doc: docs[0].doc, + path: "$.spec.conditions[*].conditionRef", + owner: reflect.TypeFor[v1.AlertPolicyConditionRef](), + fieldName: "ConditionRef", }, { - name: "v1 targetRef", - doc: docs[0].doc, - path: "$.spec.notificationTargets[*].targetRef", - fieldDoc: "TargetRef matches the [Metadata.Name](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#Metadata.Name) of an existing [AlertNotificationTarget](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertNotificationTarget).", + name: "v1 targetRef", + doc: docs[0].doc, + path: "$.spec.notificationTargets[*].targetRef", + owner: reflect.TypeFor[v1.AlertPolicyNotificationTargetRef](), + fieldName: "TargetRef", }, { - name: "v1 alertPolicyRef", - doc: docs[1].doc, - path: "$.spec.alertPolicies[*].alertPolicyRef", - fieldDoc: "AlertPolicyRef matches the [Metadata.Name](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#Metadata.Name) of an existing [AlertPolicy](https://pkg.go.dev/github.com/OpenSLO/go-sdk/pkg/openslo/v1#AlertPolicy).", + name: "v1 alertPolicyRef", + doc: docs[1].doc, + path: "$.spec.alertPolicies[*].alertPolicyRef", + owner: reflect.TypeFor[v1.SLOAlertPolicyRef](), + fieldName: "AlertPolicyRef", }, { - name: "v2alpha conditionRef", - doc: docs[2].doc, - path: "$.spec.conditions[*].conditionRef", - fieldDoc: "ConditionRef is the metadata name of the alert condition to use.", + name: "v2alpha conditionRef", + doc: docs[2].doc, + path: "$.spec.conditions[*].conditionRef", + owner: reflect.TypeFor[v2alpha.AlertPolicyConditionRef](), + fieldName: "ConditionRef", }, { - name: "v2alpha targetRef", - doc: docs[2].doc, - path: "$.spec.notificationTargets[*].targetRef", - fieldDoc: "TargetRef is the metadata name of the notification target to use.", + name: "v2alpha targetRef", + doc: docs[2].doc, + path: "$.spec.notificationTargets[*].targetRef", + owner: reflect.TypeFor[v2alpha.AlertPolicyNotificationTargetRef](), + fieldName: "TargetRef", }, { - name: "v2alpha alertPolicyRef", - doc: docs[3].doc, - path: "$.spec.alertPolicies[*].alertPolicyRef", - fieldDoc: "AlertPolicyRef is the metadata name of the alert policy to use.", + name: "v2alpha alertPolicyRef", + doc: docs[3].doc, + path: "$.spec.alertPolicies[*].alertPolicyRef", + owner: reflect.TypeFor[v2alpha.SLOAlertPolicyRef](), + fieldName: "AlertPolicyRef", }, } + resolver, err := newFieldDocResolver([]string{ + reflect.TypeFor[v1.AlertPolicyConditionRef]().PkgPath(), + reflect.TypeFor[v2alpha.AlertPolicyConditionRef]().PkgPath(), + }) + require.NoError(t, err) for _, test := range tests { t.Run(test.name, func(t *testing.T) { property := requireProperty(t, test.doc, jsonpath.Parse(test.path)) - assert.Equal(t, test.fieldDoc, property.FieldDoc) + expected, indexed := resolver.docs[fieldDocKey{ + packagePath: test.owner.PkgPath(), + typeName: test.owner.Name(), + fieldName: test.fieldName, + }] + require.True(t, indexed) + require.NotEmpty(t, expected) + assert.Equal(t, expected, property.FieldDoc) assert.Equal(t, typeDocumentation, property.TypeDoc) }) } diff --git a/pkg/openslo/v2alpha/alert_condition.go b/pkg/openslo/v2alpha/alert_condition.go index 9539623..b03fd7c 100644 --- a/pkg/openslo/v2alpha/alert_condition.go +++ b/pkg/openslo/v2alpha/alert_condition.go @@ -26,11 +26,6 @@ func NewAlertCondition(metadata Metadata, spec AlertConditionSpec) AlertConditio // AlertCondition defines when an SLO alert condition is breaching. // [AlertPolicySpec.AlertWhenBreaching] controls whether that state triggers an // alert. -// -// This type is the SDK's v2alpha representation. The living, unstable -// [OpenSLO v2alpha proposal] does not define alert objects. -// -// [OpenSLO v2alpha proposal]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md type AlertCondition struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -87,8 +82,7 @@ type AlertConditionSpec struct { // Burn rate is error-budget consumption relative to the rate allowed by the // SLO. type AlertConditionType struct { - // Kind selects the condition algorithm. This SDK requires Kind and does not - // apply the OpenSLO v1 default of [AlertConditionKindBurnRate]. + // Kind selects the condition algorithm. This SDK requires Kind. Kind AlertConditionKind `json:"kind"` // Operator compares the calculated burn rate with Threshold. Operator Operator `json:"op"` @@ -97,8 +91,7 @@ type AlertConditionType struct { // LookbackWindow sets the period for burn-rate calculation. LookbackWindow DurationShorthand `json:"lookbackWindow"` // AlertAfter sets how long the burn-rate comparison must remain true before - // the condition becomes breaching. This SDK requires AlertAfter and does not - // apply the OpenSLO v1 default of "0m". + // the condition becomes breaching. This SDK requires AlertAfter. AlertAfter DurationShorthand `json:"alertAfter"` } diff --git a/pkg/openslo/v2alpha/alert_notification_target.go b/pkg/openslo/v2alpha/alert_notification_target.go index df34895..02fb407 100644 --- a/pkg/openslo/v2alpha/alert_notification_target.go +++ b/pkg/openslo/v2alpha/alert_notification_target.go @@ -26,11 +26,6 @@ func NewAlertNotificationTarget(metadata Metadata, spec AlertNotificationTargetS // AlertNotificationTarget represents a destination for alert delivery. // The consuming implementation defines the format of // [AlertNotificationTargetSpec.Target]. -// -// This type is the SDK's v2alpha representation. The living, unstable -// [OpenSLO v2alpha proposal] does not define alert objects. -// -// [OpenSLO v2alpha proposal]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md type AlertNotificationTarget struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` diff --git a/pkg/openslo/v2alpha/alert_policy.go b/pkg/openslo/v2alpha/alert_policy.go index 8112df7..932e698 100644 --- a/pkg/openslo/v2alpha/alert_policy.go +++ b/pkg/openslo/v2alpha/alert_policy.go @@ -25,11 +25,6 @@ func NewAlertPolicy(metadata Metadata, spec AlertPolicySpec) AlertPolicy { // AlertPolicy defines which alert-condition states trigger an SLO alert. // It also defines the notification destinations for triggered alerts. -// -// This type is the SDK's v2alpha representation. The living, unstable -// [OpenSLO v2alpha proposal] does not define alert objects. -// -// [OpenSLO v2alpha proposal]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md type AlertPolicy struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` diff --git a/pkg/openslo/v2alpha/data_source.go b/pkg/openslo/v2alpha/data_source.go index 003890c..a1394cb 100644 --- a/pkg/openslo/v2alpha/data_source.go +++ b/pkg/openslo/v2alpha/data_source.go @@ -29,11 +29,6 @@ func NewDataSource(metadata Metadata, spec DataSourceSpec) DataSource { // [SLIMetricSpec.DataSourceRef] selects it by metadata name. // A metric query can instead embed [SLIMetricSpec.DataSourceSpec]. // [SLIMetricSpec.Spec] contains implementation-defined query configuration. -// -// This type is the SDK's v2alpha representation. The living, unstable -// [OpenSLO v2alpha proposal] does not define a standalone DataSource schema. -// -// [OpenSLO v2alpha proposal]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md type DataSource struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -83,7 +78,7 @@ type DataSourceSpec struct { // Description optionally summarizes the data source in at most 1,050 characters. Description string `json:"description,omitempty"` // Type identifies the metric-source type, such as Prometheus or Datadog. - // The inherited OpenSLO v1 model does not standardize the accepted Type values. + // The consuming implementation defines the accepted values. Type string `json:"type"` // ConnectionDetails contains implementation-defined connection data encoded // as JSON, such as endpoints or authentication settings. diff --git a/pkg/openslo/v2alpha/doc.go b/pkg/openslo/v2alpha/doc.go index ad21618..b9042f1 100644 --- a/pkg/openslo/v2alpha/doc.go +++ b/pkg/openslo/v2alpha/doc.go @@ -1,18 +1,14 @@ // Package v2alpha contains Go representations and validators for the unstable -// [OpenSLO v2alpha proposal]. This package can change incompatibly as the -// proposal changes. +// v2alpha API. This package can change incompatibly. // // Objects use the "openslo.com/v2alpha" API version and Kubernetes-style // [Metadata]. The metadata has one value per label and no display name. -// Compared with v1, SLO indicator fields use the names "sli" and "sliRef". +// SLO indicator fields use the names "sli" and "sliRef". // Metric source fields are "dataSourceRef", "dataSourceSpec", and "spec". // Durations support minutes, hours, days, and weeks. Threshold-metric SLOs can // have multiple objectives. // // The proposal also describes labels on individual SLO objectives, but -// [SLOObjective] does not currently expose an objective-label field. -// Where the proposal is incomplete, the exported fields, JSON tags, and -// validators define the SDK representation. -// -// [OpenSLO v2alpha proposal]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md +// [SLOObjective] does not expose an objective-label field. Exported fields, +// JSON tags, and validators define the SDK representation. package v2alpha diff --git a/pkg/openslo/v2alpha/objects.go b/pkg/openslo/v2alpha/objects.go index 9b7c932..17efec2 100644 --- a/pkg/openslo/v2alpha/objects.go +++ b/pkg/openslo/v2alpha/objects.go @@ -37,7 +37,7 @@ type Object interface { } // Metadata is the Kubernetes-style identifying metadata used by v2alpha -// objects. Unlike v1 object metadata, it does not contain a display name. +// objects. It contains a name and optional labels and annotations. type Metadata struct { // Name identifies the object when other OpenSLO objects refer to it. Name string `json:"name"` diff --git a/pkg/openslo/v2alpha/service.go b/pkg/openslo/v2alpha/service.go index 9009335..9971464 100644 --- a/pkg/openslo/v2alpha/service.go +++ b/pkg/openslo/v2alpha/service.go @@ -27,11 +27,6 @@ func NewService(metadata Metadata, spec ServiceSpec) Service { // Each [SLO] refers to a Service by metadata name through [SLOSpec.ServiceRef]. // Multiple SLOs can use the same Service name. // The SDK does not verify that a referenced Service exists. -// -// This type is the SDK's v2alpha representation. The living, unstable -// [OpenSLO v2alpha proposal] does not define a standalone Service schema. -// -// [OpenSLO v2alpha proposal]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md type Service struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` diff --git a/pkg/openslo/v2alpha/sli.go b/pkg/openslo/v2alpha/sli.go index 067863f..8839e2b 100644 --- a/pkg/openslo/v2alpha/sli.go +++ b/pkg/openslo/v2alpha/sli.go @@ -24,9 +24,7 @@ func NewSLI(metadata Metadata, spec SLISpec) SLI { } // SLI defines a derived reliability indicator calculated from one or more -// metric queries against data sources, as defined by the [OpenSLO v2alpha SLI]. -// -// [OpenSLO v2alpha SLI]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md#sli +// metric queries against data sources. type SLI struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` diff --git a/pkg/openslo/v2alpha/slo.go b/pkg/openslo/v2alpha/slo.go index 438c781..be05c8d 100644 --- a/pkg/openslo/v2alpha/slo.go +++ b/pkg/openslo/v2alpha/slo.go @@ -26,10 +26,7 @@ func NewSLO(metadata Metadata, spec SLOSpec) SLO { } } -// SLO defines a target for an SLI over a time window, as specified by the -// [OpenSLO v2alpha SLO]. -// -// [OpenSLO v2alpha SLO]: https://github.com/OpenSLO/OpenSLO/blob/e74b589cc98b98a5413611176d659a72318e7519/enhancements/v2alpha.md#slo +// SLO defines a target for an SLI over a time window. type SLO struct { APIVersion openslo.Version `json:"apiVersion"` Kind openslo.Kind `json:"kind"` @@ -98,8 +95,7 @@ type SLOSpec struct { TimeWindow []SLOTimeWindow `json:"timeWindow,omitempty"` // Objectives contains the SLO's budget targets and metric thresholds. // V2alpha permits multiple objectives for a threshold-metric SLO. - // OpenSLO v1 requires Objectives. The living v2alpha proposal does not state a - // requiredness change. This SDK accepts an omitted Objectives field. + // This SDK accepts an omitted Objectives field. Objectives []SLOObjective `json:"objectives"` // AlertPolicies contains policies associated with the SLO. // Each item must specify exactly one inline definition or metadata-name From 92a8b8c05e9d3a7c66f439604f8f73f218ade668 Mon Sep 17 00:00:00 2001 From: Mateusz Hawrus Date: Tue, 25 Aug 2026 17:35:43 +0200 Subject: [PATCH 6/6] chore: remove main_test.go --- internal/cmd/objectdoc/go.mod | 4 - internal/cmd/objectdoc/go.sum | 2 - internal/cmd/objectdoc/main_test.go | 622 ---------------------------- 3 files changed, 628 deletions(-) delete mode 100644 internal/cmd/objectdoc/main_test.go diff --git a/internal/cmd/objectdoc/go.mod b/internal/cmd/objectdoc/go.mod index 987c3ff..ef9b792 100644 --- a/internal/cmd/objectdoc/go.mod +++ b/internal/cmd/objectdoc/go.mod @@ -6,17 +6,13 @@ require ( github.com/OpenSLO/go-sdk v0.8.0 github.com/nieomylnieja/govydoc v0.0.1 github.com/nobl9/govy v0.29.0 - github.com/stretchr/testify v1.11.1 golang.org/x/tools v0.49.0 ) require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/mod v0.39.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/text v0.41.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/OpenSLO/go-sdk => ../../../. diff --git a/internal/cmd/objectdoc/go.sum b/internal/cmd/objectdoc/go.sum index ba7028b..bbcece8 100644 --- a/internal/cmd/objectdoc/go.sum +++ b/internal/cmd/objectdoc/go.sum @@ -20,8 +20,6 @@ golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= diff --git a/internal/cmd/objectdoc/main_test.go b/internal/cmd/objectdoc/main_test.go deleted file mode 100644 index 34a94d4..0000000 --- a/internal/cmd/objectdoc/main_test.go +++ /dev/null @@ -1,622 +0,0 @@ -package main - -import ( - "bytes" - "errors" - "maps" - "os" - "reflect" - "slices" - "testing" - - "github.com/nieomylnieja/govydoc/pkg/govydoc" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/nobl9/govy/pkg/govy" - "github.com/nobl9/govy/pkg/jsonpath" - - v1 "github.com/OpenSLO/go-sdk/pkg/openslo/v1" - "github.com/OpenSLO/go-sdk/pkg/openslo/v2alpha" -) - -func TestGenerateObjectDocRequiresPredicateDescriptions(t *testing.T) { - validator := govy.New[v1.Service]().When(func(v1.Service) bool { return true }) - - _, err := generateObjectDoc(validator) - - require.ErrorContains(t, err, "predicates without description found at: validator level") -} - -func TestGenerateObjectDocRejectsBlankRuleDescriptions(t *testing.T) { - validator := govy.New( - govy.For(govy.GetSelf[v1.Service]()).Rules( - govy.NewRule(func(v1.Service) error { return nil }).WithDescription(" \t"), - ), - ) - - _, err := generateObjectDoc(validator) - - require.ErrorContains(t, err, "validation rule 1 for $ in v1.Service has a blank description") -} - -func TestRegisteredValidatorsHaveCompletePlans(t *testing.T) { - for _, generator := range allDocsGenerators { - t.Run(generator.name, func(t *testing.T) { - _, err := generator.generate() - require.NoError(t, err) - }) - } -} - -func TestGenerateObjectDocsReturnsAllErrorsInGeneratorOrder(t *testing.T) { - firstErr := errors.New("first failure") - secondErr := errors.New("second failure") - secondFinished := make(chan struct{}) - generators := []objectDocGenerator{ - { - name: "first", - generate: func() (generatedObjectDoc, error) { - <-secondFinished - return generatedObjectDoc{}, firstErr - }, - }, - { - name: "second", - generate: func() (generatedObjectDoc, error) { - close(secondFinished) - return generatedObjectDoc{}, secondErr - }, - }, - } - - _, err := generateObjectDocs(generators) - - require.Error(t, err) - assert.ErrorIs(t, err, firstErr) - assert.ErrorIs(t, err, secondErr) - assert.EqualError( - t, - err, - "generate first documentation: first failure\ngenerate second documentation: second failure", - ) -} - -func TestAggregateVersionsRequiresOneDiscriminatorValue(t *testing.T) { - tests := map[string]struct { - versionValues []string - kindValues []string - expectedError string - }{ - "apiVersion without values": { - kindValues: []string{"Service"}, - expectedError: `document "Example" discriminator property $.apiVersion must have exactly one value, but it has 0`, - }, - "kind with multiple values": { - versionValues: []string{"openslo/v1"}, - kindValues: []string{"Service", "SLO"}, - expectedError: `document "Example" discriminator property $.kind must have exactly one value, but it has 2`, - }, - "kind with empty value": { - versionValues: []string{"openslo/v1"}, - kindValues: []string{""}, - expectedError: `document "Example" discriminator property $.kind has an empty value`, - }, - } - for name, test := range tests { - t.Run(name, func(t *testing.T) { - _, err := aggregateVersions([]generatedObjectDoc{ - newGeneratedObjectDoc("Example", test.versionValues, test.kindValues), - }) - - require.EqualError(t, err, test.expectedError) - }) - } -} - -func TestAggregateVersionsRejectsDuplicateAndMissingDiscriminatorPaths(t *testing.T) { - versionProperty := newDiscriminatorProperty(apiVersionPath, "openslo/v1") - kindProperty := newDiscriminatorProperty(kindPath, "Service") - tests := map[string]struct { - properties []govydoc.PropertyDoc - expectedError string - }{ - "duplicate apiVersion": { - properties: []govydoc.PropertyDoc{versionProperty, versionProperty, kindProperty}, - expectedError: `document "Example" has duplicate discriminator property $.apiVersion`, - }, - "duplicate kind": { - properties: []govydoc.PropertyDoc{versionProperty, kindProperty, kindProperty}, - expectedError: `document "Example" has duplicate discriminator property $.kind`, - }, - "missing apiVersion": { - properties: []govydoc.PropertyDoc{kindProperty}, - expectedError: `document "Example" is missing discriminator property $.apiVersion`, - }, - "missing kind": { - properties: []govydoc.PropertyDoc{versionProperty}, - expectedError: `document "Example" is missing discriminator property $.kind`, - }, - } - for name, test := range tests { - t.Run(name, func(t *testing.T) { - _, err := aggregateVersions([]generatedObjectDoc{{ - doc: govydoc.ObjectDoc{Name: "Example", Properties: test.properties}, - }}) - - require.EqualError(t, err, test.expectedError) - }) - } -} - -func TestAggregateVersionsRejectsDuplicateVersionAndKind(t *testing.T) { - docs := []generatedObjectDoc{ - newGeneratedObjectDoc("First", []string{"openslo/v1"}, []string{"Service"}), - newGeneratedObjectDoc("Second", []string{"openslo/v1"}, []string{"Service"}), - } - - _, err := aggregateVersions(docs) - - require.ErrorContains(t, err, `duplicate version and kind "openslo/v1" "Service"`) - assert.ErrorContains(t, err, `document "Second"`) - assert.ErrorContains(t, err, `already used by "First"`) -} - -func TestNormalizeRawMessages(t *testing.T) { - rawMessagePath := jsonpath.Parse("$.spec.connectionDetails") - rawMessageWildcardPath := rawMessagePath.IndexWildcard() - byteSlicePath := jsonpath.Parse("$.spec.payload") - byteSliceWildcardPath := byteSlicePath.IndexWildcard() - doc := govydoc.ObjectDoc{Properties: []govydoc.PropertyDoc{ - { - PropertyPlan: govy.PropertyPlan{ - Path: rawMessagePath, - TypeInfo: govy.TypeInfo{ - Name: jsonRawMessageName, - Kind: jsonRawMessageKind, - Package: jsonRawMessagePkg, - }, - }, - ChildrenPaths: []string{rawMessageWildcardPath.String()}, - }, - { - PropertyPlan: govy.PropertyPlan{ - Path: rawMessageWildcardPath, - TypeInfo: govy.TypeInfo{Name: "uint8", Kind: "uint8"}, - }, - }, - { - PropertyPlan: govy.PropertyPlan{ - Path: byteSlicePath, - TypeInfo: govy.TypeInfo{Name: "", Kind: jsonRawMessageKind}, - }, - ChildrenPaths: []string{byteSliceWildcardPath.String()}, - }, - { - PropertyPlan: govy.PropertyPlan{ - Path: byteSliceWildcardPath, - TypeInfo: govy.TypeInfo{Name: "uint8", Kind: "uint8"}, - }, - }, - }} - - normalizeRawMessages(&doc) - - require.Len(t, doc.Properties, 3) - rawMessage := requireProperty(t, doc, rawMessagePath) - assert.Equal(t, jsonRawMessageName, rawMessage.TypeInfo.Name) - assert.Equal(t, jsonValueKind, rawMessage.TypeInfo.Kind) - assert.Equal(t, jsonRawMessagePkg, rawMessage.TypeInfo.Package) - assert.Empty(t, rawMessage.ChildrenPaths) - assert.Nil(t, findProperty(doc, rawMessageWildcardPath)) - assert.NotNil(t, findProperty(doc, byteSliceWildcardPath)) - assert.Equal(t, jsonRawMessageKind, requireProperty(t, doc, byteSlicePath).TypeInfo.Kind) -} - -func TestNormalizeGeneratedDocsRecoversPromotedFieldDocs(t *testing.T) { - typeDocumentation := "existing type documentation" - docs := []generatedObjectDoc{ - { - rootType: reflect.TypeFor[v1.AlertPolicy](), - doc: govydoc.ObjectDoc{ - Name: "v1.AlertPolicy", - Properties: []govydoc.PropertyDoc{ - newPropertyDoc("$.spec.conditions[*].conditionRef", typeDocumentation), - newPropertyDoc("$.spec.notificationTargets[*].targetRef", typeDocumentation), - }, - }, - }, - { - rootType: reflect.TypeFor[v1.SLO](), - doc: govydoc.ObjectDoc{ - Name: "v1.SLO", - Properties: []govydoc.PropertyDoc{ - newPropertyDoc("$.spec.alertPolicies[*].alertPolicyRef", typeDocumentation), - }, - }, - }, - { - rootType: reflect.TypeFor[v2alpha.AlertPolicy](), - doc: govydoc.ObjectDoc{ - Name: "v2alpha.AlertPolicy", - Properties: []govydoc.PropertyDoc{ - newPropertyDoc("$.spec.conditions[*].conditionRef", typeDocumentation), - newPropertyDoc("$.spec.notificationTargets[*].targetRef", typeDocumentation), - }, - }, - }, - { - rootType: reflect.TypeFor[v2alpha.SLO](), - doc: govydoc.ObjectDoc{ - Name: "v2alpha.SLO", - Properties: []govydoc.PropertyDoc{ - newPropertyDoc("$.spec.alertPolicies[*].alertPolicyRef", typeDocumentation), - }, - }, - }, - } - - require.NoError(t, normalizeGeneratedDocs(docs)) - - tests := []struct { - name string - doc govydoc.ObjectDoc - path string - owner reflect.Type - fieldName string - }{ - { - name: "v1 conditionRef", - doc: docs[0].doc, - path: "$.spec.conditions[*].conditionRef", - owner: reflect.TypeFor[v1.AlertPolicyConditionRef](), - fieldName: "ConditionRef", - }, - { - name: "v1 targetRef", - doc: docs[0].doc, - path: "$.spec.notificationTargets[*].targetRef", - owner: reflect.TypeFor[v1.AlertPolicyNotificationTargetRef](), - fieldName: "TargetRef", - }, - { - name: "v1 alertPolicyRef", - doc: docs[1].doc, - path: "$.spec.alertPolicies[*].alertPolicyRef", - owner: reflect.TypeFor[v1.SLOAlertPolicyRef](), - fieldName: "AlertPolicyRef", - }, - { - name: "v2alpha conditionRef", - doc: docs[2].doc, - path: "$.spec.conditions[*].conditionRef", - owner: reflect.TypeFor[v2alpha.AlertPolicyConditionRef](), - fieldName: "ConditionRef", - }, - { - name: "v2alpha targetRef", - doc: docs[2].doc, - path: "$.spec.notificationTargets[*].targetRef", - owner: reflect.TypeFor[v2alpha.AlertPolicyNotificationTargetRef](), - fieldName: "TargetRef", - }, - { - name: "v2alpha alertPolicyRef", - doc: docs[3].doc, - path: "$.spec.alertPolicies[*].alertPolicyRef", - owner: reflect.TypeFor[v2alpha.SLOAlertPolicyRef](), - fieldName: "AlertPolicyRef", - }, - } - resolver, err := newFieldDocResolver([]string{ - reflect.TypeFor[v1.AlertPolicyConditionRef]().PkgPath(), - reflect.TypeFor[v2alpha.AlertPolicyConditionRef]().PkgPath(), - }) - require.NoError(t, err) - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - property := requireProperty(t, test.doc, jsonpath.Parse(test.path)) - expected, indexed := resolver.docs[fieldDocKey{ - packagePath: test.owner.PkgPath(), - typeName: test.owner.Name(), - fieldName: test.fieldName, - }] - require.True(t, indexed) - require.NotEmpty(t, expected) - assert.Equal(t, expected, property.FieldDoc) - assert.Equal(t, typeDocumentation, property.TypeDoc) - }) - } -} - -func TestRecoverFieldDocsReportsFieldsMissingFromASTIndex(t *testing.T) { - fixtureType := reflect.TypeFor[fieldDocRecoveryFixture]() - fields := []struct { - name string - path string - }{ - {name: "Documented", path: "$.documented"}, - {name: "Undocumented", path: "$.undocumented"}, - {name: "MissingFirst", path: "$.missingFirst"}, - {name: "MissingSecond", path: "$.missingSecond"}, - } - doc := govydoc.ObjectDoc{Name: "FixtureDoc"} - origins := make(map[string]fieldOrigin, len(fields)) - resolver := &fieldDocResolver{docs: make(map[fieldDocKey]string)} - for _, item := range fields { - field, ok := fixtureType.FieldByName(item.name) - require.True(t, ok) - doc.Properties = append(doc.Properties, newPropertyDoc(item.path, "")) - origins[item.path] = fieldOrigin{owner: fixtureType, field: field} - key := fieldDocKey{ - packagePath: fixtureType.PkgPath(), - typeName: fixtureType.Name(), - fieldName: field.Name, - } - switch item.name { - case "Documented": - resolver.docs[key] = "Recovered field documentation." - case "Undocumented": - resolver.docs[key] = "" - } - } - - err := recoverFieldDocs(&doc, origins, resolver) - - require.Error(t, err) - assert.ErrorContains(t, err, "recover field documentation for FixtureDoc at $.missingFirst") - assert.ErrorContains(t, err, "fieldDocRecoveryFixture.MissingFirst is missing from the AST field index") - assert.ErrorContains(t, err, "recover field documentation for FixtureDoc at $.missingSecond") - assert.ErrorContains(t, err, "fieldDocRecoveryFixture.MissingSecond is missing from the AST field index") - assert.Equal(t, "Recovered field documentation.", requireProperty(t, doc, jsonpath.Parse("$.documented")).FieldDoc) - assert.Empty(t, requireProperty(t, doc, jsonpath.Parse("$.undocumented")).FieldDoc) -} - -func TestRecoverFieldDocsRejectsOriginlessRealPaths(t *testing.T) { - fixtureType := reflect.TypeFor[fieldDocRecoveryFixture]() - field, ok := fixtureType.FieldByName("Documented") - require.True(t, ok) - origins := map[string]fieldOrigin{ - "$.labels": {owner: fixtureType, field: field}, - } - doc := govydoc.ObjectDoc{ - Name: "FixtureDoc", - Properties: []govydoc.PropertyDoc{ - newPropertyDoc("$", ""), - newPropertyDoc("$.labels[*]", ""), - newPropertyDoc("$.labels.*", ""), - newPropertyDoc("$.labels.*~", ""), - newPropertyDoc("$.labels.*[*]", ""), - newPropertyDoc("$.labels.*~[*]", ""), - newPropertyDoc("$.items[*].promoted", ""), - }, - } - resolver := &fieldDocResolver{docs: make(map[fieldDocKey]string)} - - err := recoverFieldDocs(&doc, origins, resolver) - - require.EqualError( - t, - err, - "recover field documentation for FixtureDoc at $.items[*].promoted: path has no Go field origin", - ) -} - -func TestGeneratedDocsExposeCrossCuttingContracts(t *testing.T) { - versions, err := generateVersions() - require.NoError(t, err) - - for version, documents := range versions { - for kind, document := range documents { - t.Run(version+"/"+kind+"/description", func(t *testing.T) { - requirePropertyRule( - t, - requireProperty(t, document, jsonpath.Parse("$.spec.description")), - govy.ErrorCode("optional"), - ) - }) - } - } - - for _, version := range []Version{"openslo/v1", "openslo.com/v2alpha"} { - for kind, document := range versions[version] { - for _, path := range []string{"$.metadata.labels", "$.metadata.annotations"} { - t.Run(version+"/"+kind+"/"+path, func(t *testing.T) { - requirePropertyRule( - t, - requireProperty(t, document, jsonpath.Parse(path)), - govy.ErrorCode("optional"), - ) - }) - } - } - - policy := versions[version]["AlertPolicy"] - for _, path := range []string{ - "$.spec.alertWhenNoData", - "$.spec.alertWhenBreaching", - "$.spec.alertWhenResolved", - } { - t.Run(version+"/AlertPolicy/"+path, func(t *testing.T) { - requirePropertyRule( - t, - requireProperty(t, policy, jsonpath.Parse(path)), - govy.ErrorCode("optional"), - ) - }) - } - } - - timeWindowPaths := map[Version]string{ - "openslo/v1alpha": "$.spec.timeWindows[*]", - "openslo/v1": "$.spec.timeWindow[*]", - "openslo.com/v2alpha": "$.spec.timeWindow[*]", - } - for version, path := range timeWindowPaths { - t.Run(version+"/SLO/calendar invariant", func(t *testing.T) { - requirePropertyRuleDescription( - t, - requireProperty(t, versions[version]["SLO"], jsonpath.Parse(path)), - "'calendar' must be set when 'isRolling' is false and cannot be set when 'isRolling' is true", - ) - }) - } -} - -func TestGenerateVersionsMatchesCanonicalManifest(t *testing.T) { - first, err := generateVersions() - require.NoError(t, err) - second, err := generateVersions() - require.NoError(t, err) - - var firstOutput bytes.Buffer - require.NoError(t, encodeVersions(&firstOutput, first)) - var secondOutput bytes.Buffer - require.NoError(t, encodeVersions(&secondOutput, second)) - require.True( - t, - bytes.Equal(firstOutput.Bytes(), secondOutput.Bytes()), - "successive generations produced different serialized output", - ) - - expectedKinds := map[Version][]Kind{ - "openslo/v1alpha": {"SLO", "Service"}, - "openslo/v1": { - "AlertCondition", - "AlertNotificationTarget", - "AlertPolicy", - "DataSource", - "SLI", - "SLO", - "Service", - }, - "openslo.com/v2alpha": { - "AlertCondition", - "AlertNotificationTarget", - "AlertPolicy", - "DataSource", - "SLI", - "SLO", - "Service", - }, - } - assert.Equal(t, expectedKinds, collectVersionKinds(first)) - - connectionDetailsPath := jsonpath.Parse("$.spec.connectionDetails") - connectionDetailsWildcardPath := connectionDetailsPath.IndexWildcard() - for _, version := range []Version{"openslo/v1", "openslo.com/v2alpha"} { - documents, ok := first[version] - require.True(t, ok, "version %s not found", version) - dataSource, ok := documents["DataSource"] - require.True(t, ok, "DataSource not found for version %s", version) - connectionDetails := requireProperty(t, dataSource, connectionDetailsPath) - assert.Equal(t, jsonRawMessageName, connectionDetails.TypeInfo.Name) - assert.Equal(t, jsonValueKind, connectionDetails.TypeInfo.Kind) - assert.Equal(t, jsonRawMessagePkg, connectionDetails.TypeInfo.Package) - assert.NotContains(t, connectionDetails.ChildrenPaths, connectionDetailsWildcardPath.String()) - assert.Nil(t, findProperty(dataSource, connectionDetailsWildcardPath)) - } - - checkedIn, err := os.ReadFile("../../../docs/manifest.json") - require.NoError(t, err) - require.True( - t, - bytes.Equal(checkedIn, firstOutput.Bytes()), - "docs/manifest.json is stale: checked-in size %d, generated size %d. Run make generate", - len(checkedIn), - firstOutput.Len(), - ) -} - -type fieldDocRecoveryFixture struct { - Documented string `json:"documented"` - Undocumented string `json:"undocumented"` - MissingFirst string `json:"missingFirst"` - MissingSecond string `json:"missingSecond"` -} - -func newGeneratedObjectDoc(name string, versionValues, kindValues []string) generatedObjectDoc { - return generatedObjectDoc{doc: govydoc.ObjectDoc{ - Name: name, - Properties: []govydoc.PropertyDoc{ - newDiscriminatorProperty(apiVersionPath, versionValues...), - newDiscriminatorProperty(kindPath, kindValues...), - }, - }} -} - -func newDiscriminatorProperty(path jsonpath.Path, values ...string) govydoc.PropertyDoc { - return govydoc.PropertyDoc{PropertyPlan: govy.PropertyPlan{ - Path: path, - Values: values, - }} -} - -func collectVersionKinds(versions Versions) map[Version][]Kind { - kinds := make(map[Version][]Kind, len(versions)) - for version, documents := range versions { - kinds[version] = slices.Sorted(maps.Keys(documents)) - } - return kinds -} - -func newPropertyDoc(path, typeDoc string) govydoc.PropertyDoc { - return govydoc.PropertyDoc{ - PropertyPlan: govy.PropertyPlan{Path: jsonpath.Parse(path)}, - TypeDoc: typeDoc, - } -} - -func requireProperty(t *testing.T, doc govydoc.ObjectDoc, path jsonpath.Path) *govydoc.PropertyDoc { - t.Helper() - property := findProperty(doc, path) - require.NotNil(t, property, "property %s not found", path) - return property -} - -func findProperty(doc govydoc.ObjectDoc, path jsonpath.Path) *govydoc.PropertyDoc { - for i := range doc.Properties { - if doc.Properties[i].Path.Equal(path) { - return &doc.Properties[i] - } - } - return nil -} - -func requirePropertyRule( - t *testing.T, - property *govydoc.PropertyDoc, - errorCode govy.ErrorCode, - conditions ...string, -) { - t.Helper() - for _, rule := range property.Rules { - if rule.ErrorCode == errorCode && slices.Equal(rule.Conditions, conditions) { - return - } - } - t.Fatalf( - "property %s has no %q rule with conditions %v", - property.Path, - errorCode, - conditions, - ) -} - -func requirePropertyRuleDescription( - t *testing.T, - property *govydoc.PropertyDoc, - description string, -) { - t.Helper() - for _, rule := range property.Rules { - if rule.Description == description { - return - } - } - t.Fatalf( - "property %s has no rule with description %q", - property.Path, - description, - ) -}