Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add schema transformation functionality to resource creation and merge #4503

Closed
wants to merge 55 commits into from

Conversation

MrAlias
Copy link
Contributor

@MrAlias MrAlias commented Sep 12, 2023

Currently, the resource package fails for conflicting schema URL merges in the following way:

  • If New is used with multiple detectors that detect resources at with different schema URLs
  • If Detect is used with multiple detectors that detect resources at with different schema URLs
  • If Merge is provided two resources with different schema URLs

All of these errors can be reduced to the last one: the New function will call the underlying detect which itself calls Merge for multiple resources.

This fixes all of these failures by upgrading the older resources prior to merging using schema transformations.

There are no API changes added at this time. The translation is limited to only upgrading and only in the Merge function. It is possible to add more customizable translations that support more schema (other than just OpenTelemetry ones), but that is left for another PR once there is deemed enough user support.

Fixes #2341
Fixes #3769
Fixes #4476
Fixes open-telemetry/opentelemetry-go-contrib#3657

Design

Based on https://github.com/MrAlias/otel-schema-utils/tree/main with the following notable differences:

  • Update the Merge function in sdk/resource to upgrade the less of the two passed resources
    • Detect calls detect which uses Merge to resolve resources from multiple detectors
    • New uses detect to create new resources with Detectors
    • Only upgrades are currently supported
  • Add the sdk/resource/internal/schema package
    • The Version function parses and converts schema URL to semconv.Version types.
    • The Upgrade function allows the upgrade of a resources attributes using a statically defined transform from an OTel schema
      • The transforms are statically defined using a generation script in generation
        • The latest OTel schema is held in code in the schema directory
          • The a fetch.go script is included to fetch a schema and can be used for upgrade
      • A render package is included that will render and format Go to generate the template.go file with the static transforms
    • No panics during start ups, which would be the case for dynamically loading these schema
    • No HTTP calls during runtime, the HTTP fetch operations are performed by our CI system or locally

@MrAlias MrAlias added the area:resources Part of OpenTelemetry resources label Sep 12, 2023
@codecov
Copy link

codecov bot commented Sep 13, 2023

Codecov Report

Merging #4503 (74e8197) into main (a5190f6) will decrease coverage by 0.1%.
The diff coverage is 79.0%.

Impacted file tree graph

@@           Coverage Diff           @@
##            main   #4503     +/-   ##
=======================================
- Coverage   81.3%   81.3%   -0.1%     
=======================================
  Files        220     222      +2     
  Lines      17678   17825    +147     
=======================================
+ Hits       14386   14499    +113     
- Misses      2992    3020     +28     
- Partials     300     306      +6     
Files Changed Coverage
...resource/internal/schema/generate/render/render.go 42.5%
sdk/resource/internal/schema/schema.go 100.0%
sdk/resource/resource.go 100.0%

@MrAlias MrAlias marked this pull request as ready for review September 14, 2023 14:52
@MrAlias MrAlias marked this pull request as ready for review September 15, 2023 19:54
github.com/go-logr/logr v1.2.4
github.com/google/go-cmp v0.5.9
github.com/stretchr/testify v1.8.4
go.opentelemetry.io/otel v1.19.0-rc.1
go.opentelemetry.io/otel/schema v0.0.6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are introducing on a new unstable Go module.
If I am not mistaken I have found that this module is currently used by:

Still somebody else can potentially use go.opentelemetry.io/otel/schema and then we can have a problem in case there would be some incompatible changes in go.opentelemetry.io/otel/schema.

Currently, I have two ideas how we could resolve the issue:

A) Stabilize go.opentelemetry.io/otel/schema.
B) Use gotmpl to copy (vendor) ./schema content.

Are there any reasons why not going with option A? Any other ideas?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Private project @MrAlias ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC you need to be an OTel member to see the project.

sdk/go.mod Show resolved Hide resolved
Comment on lines +15 to +51
package cmd

import (
"bytes"
"io"
"os"
"testing"

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

sUtil "go.opentelemetry.io/otel/schema/v1.1"
)

func TestRender(t *testing.T) {
schemaF, err := os.Open("./testdata/schema.yaml")
require.NoError(t, err)
defer schemaF.Close()

s, err := sUtil.Parse(schemaF)
require.NoError(t, err)

wantF, err := os.Open("./testdata/transforms.go")
require.NoError(t, err)

var want bytes.Buffer
_, err = io.Copy(&want, wantF)
require.NoError(t, err)

e, err := entries(s)
require.NoError(t, err)

var got bytes.Buffer
require.NoError(t, render(&got, e))

assert.Equal(t, want.String(), got.String())
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. schema "go.opentelemetry.io/otel/schema/v1.1" instead of sUtil "go.opentelemetry.io/otel/schema/v1.1" (it is even suggested in https://pkg.go.dev/go.opentelemetry.io/otel/schema
  2. Use os.ReadFile
  3. Move want preparation on the top.
Suggested change
package cmd
import (
"bytes"
"io"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
sUtil "go.opentelemetry.io/otel/schema/v1.1"
)
func TestRender(t *testing.T) {
schemaF, err := os.Open("./testdata/schema.yaml")
require.NoError(t, err)
defer schemaF.Close()
s, err := sUtil.Parse(schemaF)
require.NoError(t, err)
wantF, err := os.Open("./testdata/transforms.go")
require.NoError(t, err)
var want bytes.Buffer
_, err = io.Copy(&want, wantF)
require.NoError(t, err)
e, err := entries(s)
require.NoError(t, err)
var got bytes.Buffer
require.NoError(t, render(&got, e))
assert.Equal(t, want.String(), got.String())
}
package cmd
import (
"bytes"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
schema "go.opentelemetry.io/otel/schema/v1.1"
)
func TestRender(t *testing.T) {
want, err := os.ReadFile("./testdata/transforms.go")
require.NoError(t, err)
schemaF, err := os.Open("./testdata/schema.yaml")
require.NoError(t, err)
defer schemaF.Close()
s, err := schema.Parse(schemaF)
require.NoError(t, err)
e, err := entries(s)
require.NoError(t, err)
var got bytes.Buffer
require.NoError(t, render(&got, e))
assert.Equal(t, string(want), got.String())
}

"flag"
"log"

sUtil "go.opentelemetry.io/otel/schema/v1.1"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you change the alias to schema as suggested https://pkg.go.dev/go.opentelemetry.io/otel/schema

// See the License for the specific language governing permissions and
// limitations under the License.

package main
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add some package documentation that would describe what this tool is doing and why it was created.

// See the License for the specific language governing permissions and
// limitations under the License.

package main
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add some package documentation that would describe what this tool is doing and why it was created.

Also am I correct the usage of it is not currently implemented? Shouldn't it be invoked as part of make semconv-generate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll update RELEASING.md to include it as a manual step for now.

return err
}

src, err := format.Source(out.Bytes())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are shadowing const src = "transforms.go.tmpl" here. Maybe we should rename the const?

Comment on lines +161 to +162
// prior to the merge using the OpenTelemetry schema resource section
// (https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/schemas/file_format_v1.1.0.md#resources-section)
Copy link
Member

@pellared pellared Sep 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Update the tag
  2. Make it an URL
Suggested change
// prior to the merge using the OpenTelemetry schema resource section
// (https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/schemas/file_format_v1.1.0.md#resources-section)
// prior to the merge using
// [the OpenTelemetry schema resource section](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.25.0/specification/schemas/file_format_v1.1.0.md#resources-section)

// (https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/schemas/file_format_v1.1.0.md#resources-section)
// for the translation.
//
// If either of the resources have an invalid SchemaURL (non-OpenTelemetry or
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to call out that "newer" versions of SchemaURL are also not supported. E.g. this will not magically support https://opentelemetry.io/schemas/1.22.0 once https://github.com/open-telemetry/semantic-conventions is released. I am not sure how to describe it in a readable way...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't we upgrade and release support for v1.22.0 when it is released?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will. My point is that somebody may not understand that the support is "compiled"

Copy link
Member

@pellared pellared Sep 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like

If either of the resources have an unknown SchemaURL (only specific versions of OpenTelemetry schemas are supported), [...]

name: "BothUnknownSchemas",
a: resource.NewWithAttributes("https://localhost/1", kv41),
b: resource.NewWithAttributes("https://localhost/2", kv42),
},
Copy link
Member

@pellared pellared Sep 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about adding this scenario

Suggested change
},
},
{
name: "NotSupportedOTelSchema",
a: resource.NewWithAttributes("https://opentelemetry.io/schemas/1.999.0", kv41),
b: resource.NewWithAttributes("https://opentelemetry.io/schemas/1.0.0", kv42),
},

Also I think in such scenario we should either:

a) simply return error that this semconv version is not supported and that the user can try using a new version of the OTel Go SDK
b) prefer all "known" translations (current implementation) and additionally return the error - it should be possible for the user to determine this error type so he could ignore the error returned in this scenario

@jufemaiz
Copy link

👀

@pellared pellared changed the title Add schema translation functionality to resource creation and merge Add schema transformation functionality to resource creation and merge Dec 5, 2023
@pellared pellared marked this pull request as draft December 12, 2023 09:33
@MrAlias
Copy link
Contributor Author

MrAlias commented Jan 31, 2024

Based on the resource merge specification:

The resulting resource MUST have all attributes that are on any of the two input resources. If a key exists on both the old and updating resource, the value of the updating resource MUST be picked (even if the updated value is empty).

Doing anything other than preserving all the keys provided to merge will be non-compliant.

@MrAlias MrAlias closed this Jan 31, 2024
@MrAlias MrAlias mentioned this pull request Jan 31, 2024
@pellared
Copy link
Member

Doing anything other than preserving all the keys provided to merge will be non-compliant.

I agree. I think that we can consider enhancing the specification so that (examples, brainstorming):

  1. Merge can do transformations
  2. Add an optional parameter to Merge that instructs to additionally perform resource transformations
  3. Add new operation e.g. Transform that takes a resource and target scheme URL that tries to apply transformations for given resource.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:resources Part of OpenTelemetry resources
Projects
None yet
5 participants