Skip to content

Commit

Permalink
Introduce "split" metric schema transformation (#2999)
Browse files Browse the repository at this point in the history
This is a new transformation type that allows to describe a change
where a metric is converted to several other metrics by eliminating
an attribute.

An example of such change that happened recently is this:
open-telemetry/opentelemetry-specification#2617

This PR implements specification change open-telemetry/opentelemetry-specification#2653

This PR creates package v1.1 for the new functionality. The old package v1.0
remains unchanged.
  • Loading branch information
shbieng authored Dec 18, 2023
1 parent 85092ca commit cc4ace3
Show file tree
Hide file tree
Showing 14 changed files with 741 additions and 153 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

- Add support for `opentracing.TextMap` format in the `Inject` and `Extract` methods
of the `"go.opentelemetry.io/otel/bridge/opentracing".BridgeTracer` type. (#2911)
- Add support for Schema Files format 1.1.x (metric "split" transform). (#2999)

### Changed

Expand Down
4 changes: 2 additions & 2 deletions schema/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ then import the corresponding package and use the `Parse` or `ParseFile` functio
like this:

```go
import schema "go.opentelemetry.io/otel/schema/v1.0"
import schema "go.opentelemetry.io/otel/schema/v1.1"

// Load the schema from a file in v1.0.x file format.
// Load the schema from a file in v1.1.x file format.
func loadSchemaFromFile() error {
telSchema, err := schema.ParseFile("schema-file.yaml")
if err != nil {
Expand Down
74 changes: 74 additions & 0 deletions schema/internal/parser_checks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package internal // import "go.opentelemetry.io/otel/schema/internal"

import (
"errors"
"fmt"
"net/url"
"strconv"
"strings"

"github.com/Masterminds/semver/v3"
)

// CheckFileFormatField validates the file format field according to the rules here:
// https://github.com/open-telemetry/oteps/blob/main/text/0152-telemetry-schemas.md#schema-file-format-number
func CheckFileFormatField(fileFormat string, supportedFormatMajor, supportedFormatMinor int) error {
// Verify that the version number in the file is a semver.
fileFormatParsed, err := semver.StrictNewVersion(fileFormat)
if err != nil {
return fmt.Errorf(
"invalid schema file format version number %q (expected semver): %w",
fileFormat, err,
)
}

// Check that the major version number in the file is the same as what we expect.
if fileFormatParsed.Major() != uint64(supportedFormatMajor) {
return fmt.Errorf(
"this library cannot parse file formats with major version other than %v",
supportedFormatMajor,
)
}

// Check that the file minor version number is not greater than
// what is requested supports.
if fileFormatParsed.Minor() > uint64(supportedFormatMinor) {
supportedFormatMajorMinor := strconv.Itoa(supportedFormatMajor) + "." +
strconv.Itoa(supportedFormatMinor) // 1.0

return fmt.Errorf(
"unsupported schema file format minor version number, expected no newer than %v, got %v",
supportedFormatMajorMinor+".x", fileFormat,
)
}

// Patch, prerelease and metadata version number does not matter, so we don't check it.

return nil
}

// CheckSchemaURL verifies that schemaURL is valid.
func CheckSchemaURL(schemaURL string) error {
if strings.TrimSpace(schemaURL) == "" {
return errors.New("schema_url field is missing")
}

if _, err := url.Parse(schemaURL); err != nil {
return fmt.Errorf("invalid URL specified in schema_url field: %w", err)
}
return nil
}
41 changes: 41 additions & 0 deletions schema/internal/parser_checks_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package internal // import "go.opentelemetry.io/otel/schema/internal"

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestCheckFileFormatField(t *testing.T) {
// Invalid file format version numbers.
assert.Error(t, CheckFileFormatField("not a semver", 1, 0))
assert.Error(t, CheckFileFormatField("2.0.0", 1, 0))
assert.Error(t, CheckFileFormatField("1.1.0", 1, 0))

assert.Error(t, CheckFileFormatField("1.2.0", 1, 1))

// Valid cases.
assert.NoError(t, CheckFileFormatField("1.0.0", 1, 0))
assert.NoError(t, CheckFileFormatField("1.0.1", 1, 0))
assert.NoError(t, CheckFileFormatField("1.0.10000-alpha+4857", 1, 0))

assert.NoError(t, CheckFileFormatField("1.0.0", 1, 1))
assert.NoError(t, CheckFileFormatField("1.0.1", 1, 1))
assert.NoError(t, CheckFileFormatField("1.0.10000-alpha+4857", 1, 1))
assert.NoError(t, CheckFileFormatField("1.1.0", 1, 1))
assert.NoError(t, CheckFileFormatField("1.1.1", 1, 1))
}
56 changes: 6 additions & 50 deletions schema/v1.0/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,12 @@
package schema // import "go.opentelemetry.io/otel/schema/v1.0"

import (
"fmt"
"io"
"net/url"
"os"
"strconv"
"strings"

"github.com/Masterminds/semver/v3"
"gopkg.in/yaml.v2"

"go.opentelemetry.io/otel/schema/internal"
"go.opentelemetry.io/otel/schema/v1.0/ast"
)

Expand All @@ -34,10 +30,6 @@ const supportedFormatMajor = 1
// Maximum minor version number that this library supports.
const supportedFormatMinor = 0

// Maximum major+minor version number that this library supports, as a string.
var supportedFormatMajorMinor = strconv.Itoa(supportedFormatMajor) + "." +
strconv.Itoa(supportedFormatMinor) // 1.0

// ParseFile a schema file. schemaFilePath is the file path.
func ParseFile(schemaFilePath string) (*ast.Schema, error) {
file, err := os.Open(schemaFilePath)
Expand All @@ -56,51 +48,15 @@ func Parse(schemaFileContent io.Reader) (*ast.Schema, error) {
return nil, err
}

if err := checkFileFormatField(ts.FileFormat); err != nil {
err = internal.CheckFileFormatField(ts.FileFormat, supportedFormatMajor, supportedFormatMinor)
if err != nil {
return nil, err
}

if strings.TrimSpace(ts.SchemaURL) == "" {
return nil, fmt.Errorf("schema_url field is missing")
}

if _, err := url.Parse(ts.SchemaURL); err != nil {
return nil, fmt.Errorf("invalid URL specified in schema_url field: %w", err)
}

return &ts, nil
}

// checkFileFormatField validates the file format field according to the rules here:
// https://github.com/open-telemetry/oteps/blob/main/text/0152-telemetry-schemas.md#schema-file-format-number
func checkFileFormatField(fileFormat string) error {
// Verify that the version number in the file is a semver.
fileFormatParsed, err := semver.StrictNewVersion(fileFormat)
err = internal.CheckSchemaURL(ts.SchemaURL)
if err != nil {
return fmt.Errorf(
"invalid schema file format version number %q (expected semver): %w",
fileFormat, err,
)
}

// Check that the major version number in the file is the same as what we expect.
if fileFormatParsed.Major() != supportedFormatMajor {
return fmt.Errorf(
"this library cannot parse file formats with major version other than %v",
supportedFormatMajor,
)
}

// Check that the file minor version number is not greater than
// what is requested supports.
if fileFormatParsed.Minor() > supportedFormatMinor {
return fmt.Errorf(
"unsupported schema file format minor version number, expected no newer than %v, got %v",
supportedFormatMajorMinor+".x", fileFormat,
)
return nil, err
}

// Patch, prerelease and metadata version number does not matter, so we don't check it.

return nil
return &ts, nil
}
Loading

0 comments on commit cc4ace3

Please sign in to comment.