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 a resource detector for Azure VMs #5422

Merged
merged 34 commits into from
Jun 16, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ec1a30f
Add Azure resource detectory
pyohannes Apr 22, 2024
643207b
Fix go.sum
pyohannes Apr 22, 2024
859b363
PR comments
pyohannes Apr 22, 2024
15688a2
Change option handling
pyohannes Apr 23, 2024
80d0d11
Add changelog
pyohannes Apr 23, 2024
f1b2704
Add README
pyohannes Apr 23, 2024
115b941
Add pyohannes as code owner for Azure resource detectors
pyohannes May 27, 2024
deaed2e
Add missing attributes
pyohannes May 27, 2024
dacbb2b
Fix changelog
pyohannes May 27, 2024
9837d40
PR comments
pyohannes May 29, 2024
c268566
Linter issues
pyohannes May 29, 2024
c157aa0
Linter issues
pyohannes May 29, 2024
edb01a0
Update detectors/azure/vm/vm.go
pyohannes May 30, 2024
6fc2bd8
Update detectors/azure/vm/README.md
pyohannes May 30, 2024
336463e
PR comments
pyohannes May 30, 2024
887a8dd
PR comments
pyohannes May 31, 2024
39d8f62
Linter issues
pyohannes May 31, 2024
103dcb5
Linter issues
pyohannes May 31, 2024
8b0b366
Make struct public
pyohannes May 31, 2024
001940d
Add missing comment
pyohannes May 31, 2024
507cdf6
Fix linter issues
pyohannes May 31, 2024
7e0fc07
Merge branch 'main' into resource-detector-azure
pyohannes Jun 3, 2024
930f84c
Comment broken link
pyohannes Jun 3, 2024
c8b1212
Use context in HTTP request
pyohannes Jun 3, 2024
2c4b80e
Apply suggestions from code review
pyohannes Jun 3, 2024
2972ad2
Update module files
pyohannes Jun 3, 2024
315ae57
Add Azure VM detector with version v0.0.1
pyohannes Jun 3, 2024
4d45d2a
Merge branch 'main' into resource-detector-azure
pyohannes Jun 4, 2024
a507369
Merge branch 'main' into resource-detector-azure
pyohannes Jun 4, 2024
e78faca
Merge branch 'main' into resource-detector-azure
pyohannes Jun 5, 2024
726e7ab
Merge branch 'main' into resource-detector-azure
pyohannes Jun 6, 2024
3a972cd
Merge branch 'main' into resource-detector-azure
pyohannes Jun 13, 2024
e6b0492
Merge branch 'main' into resource-detector-azure
MrAlias Jun 14, 2024
a71cb41
Merge branch 'main' into resource-detector-azure
MrAlias Jun 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Added

- The `go.opentelemetry.io/contrib/config` add support to configure periodic reader interval and timeout. (#5661)
- Add the new `go.opentelemetry.io/contrib/detectors/azure/azurevm` package to provide a resource detector for Azure VMs. (#5422)

### Fixed

Expand Down
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ detectors/aws/ec2 @open-te
detectors/aws/ecs @open-telemetry/go-approvers @pyohannes
detectors/aws/eks @open-telemetry/go-approvers @pyohannes
detectors/aws/lambda @open-telemetry/go-approvers
detectors/azure/ @open-telemetry/go-approvers @pyohannes
detectors/gcp/ @open-telemetry/go-approvers @dashpole

exporters/autoexport @open-telemetry/go-approvers @MikeGoldsmith @pellared
Expand Down
3 changes: 3 additions & 0 deletions detectors/azure/azurevm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Azure VM Resource detector
pyohannes marked this conversation as resolved.
Show resolved Hide resolved

[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/contrib/detectors/azure/azurevm)](https://pkg.go.dev/go.opentelemetry.io/contrib/detectors/azure/azurevm)
pyohannes marked this conversation as resolved.
Show resolved Hide resolved
25 changes: 25 additions & 0 deletions detectors/azure/azurevm/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

/*
Package azurevm provides a [resource.Detector] which supports detecting
attributes specific to Azure VMs.
According to semantic conventions for [host], [cloud], and [os] attributes,
each of the following attributes is added if it is available:
- cloud.provider
- cloud.platform
- cloud.region
- cloud.resource_id
- host.id
- host.name
- host.type
- os.type
- os.version
[host]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/resource/host.md
[cloud]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/resource/cloud.md
[os]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/resource/os.md
*/
package azurevm // import "go.opentelemetry.io/contrib/detectors/azure/azurevm"
22 changes: 22 additions & 0 deletions detectors/azure/azurevm/example_new_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package azurevm_test

import (
"context"
"fmt"

"go.opentelemetry.io/contrib/detectors/azure/azurevm"
)

func ExampleNew() {
azureVMResourceDetector := azurevm.New()
resource, err := azureVMResourceDetector.Detect(context.Background())
if err != nil {
panic(err)
}

// Now, you can use the resource (e.g. pass it to a tracer or meter provider).
fmt.Println(resource.SchemaURL())
}
20 changes: 20 additions & 0 deletions detectors/azure/azurevm/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module go.opentelemetry.io/contrib/detectors/azure/azurevm

go 1.21

require (
github.com/stretchr/testify v1.9.0
go.opentelemetry.io/otel v1.25.0
go.opentelemetry.io/otel/sdk v1.25.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opentelemetry.io/otel/metric v1.25.0 // indirect
go.opentelemetry.io/otel/trace v1.25.0 // indirect
golang.org/x/sys v0.18.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
27 changes: 27 additions & 0 deletions detectors/azure/azurevm/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
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/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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k=
go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg=
go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA=
go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s=
go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo=
go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw=
go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM=
go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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=
116 changes: 116 additions & 0 deletions detectors/azure/azurevm/vm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package azurevm // import "go.opentelemetry.io/contrib/detectors/azure/azurevm"

import (
"context"
"encoding/json"
"errors"
"io"
"net/http"

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
pyohannes marked this conversation as resolved.
Show resolved Hide resolved
)

const (
defaultAzureVMMetadataEndpoint = "http://169.254.169.254/metadata/instance/compute?api-version=2021-12-13&format=json"
)
pyohannes marked this conversation as resolved.
Show resolved Hide resolved

// AzureVMResourceDetector collects resource information of Azure VMs.
pyohannes marked this conversation as resolved.
Show resolved Hide resolved
type ResourceDetector struct {
endpoint string
}

type vmMetadata struct {
VMId *string `json:"vmId"`
Location *string `json:"location"`
ResourceId *string `json:"resourceId"`
Name *string `json:"name"`
VMSize *string `json:"vmSize"`
OsType *string `json:"osType"`
Version *string `json:"version"`
}

// New returns a [resource.Detector] that will detect Azure VM resources.
pyohannes marked this conversation as resolved.
Show resolved Hide resolved
func New() *ResourceDetector {
return &ResourceDetector{defaultAzureVMMetadataEndpoint}
}

// Detect detects associated resources when running on an Azure VM.
func (detector *ResourceDetector) Detect(ctx context.Context) (*resource.Resource, error) {
jsonMetadata, runningInAzure, err := detector.getJSONMetadata()
pyohannes marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
dashpole marked this conversation as resolved.
Show resolved Hide resolved
if !runningInAzure {
return resource.Empty(), nil
}

return nil, err
}

var metadata vmMetadata
err = json.Unmarshal(jsonMetadata, &metadata)
if err != nil {
return nil, err
}

attributes := []attribute.KeyValue{
semconv.CloudProviderAzure,
semconv.CloudPlatformAzureVM,
}

if metadata.VMId != nil {
attributes = append(attributes, semconv.HostID(*metadata.VMId))
}
if metadata.Location != nil {
attributes = append(attributes, semconv.CloudRegion(*metadata.Location))
}
if metadata.ResourceId != nil {
attributes = append(attributes, semconv.CloudResourceID(*metadata.ResourceId))
}
if metadata.Name != nil {
attributes = append(attributes, semconv.HostName(*metadata.Name))
}
if metadata.VMSize != nil {
attributes = append(attributes, semconv.HostType(*metadata.VMSize))
}
if metadata.OsType != nil {
attributes = append(attributes, semconv.OSTypeKey.String(*metadata.OsType))
}
if metadata.Version != nil {
attributes = append(attributes, semconv.OSVersion(*metadata.Version))
}

return resource.NewWithAttributes(semconv.SchemaURL, attributes...), nil
}

func (detector *ResourceDetector) getJSONMetadata() ([]byte, bool, error) {
pTransport := &http.Transport{Proxy: nil}

client := http.Client{Transport: pTransport}

req, err := http.NewRequest("GET", detector.endpoint, nil)
if err != nil {
return nil, false, err

Check warning on line 96 in detectors/azure/azurevm/vm.go

View check run for this annotation

Codecov / codecov/patch

detectors/azure/azurevm/vm.go#L96

Added line #L96 was not covered by tests
}

req.Header.Add("Metadata", "True")

resp, err := client.Do(req)
if err != nil {
return nil, false, err

Check warning on line 103 in detectors/azure/azurevm/vm.go

View check run for this annotation

Codecov / codecov/patch

detectors/azure/azurevm/vm.go#L103

Added line #L103 was not covered by tests
}

defer resp.Body.Close()

if resp.StatusCode == http.StatusOK {
bytes, err := io.ReadAll(resp.Body)
return bytes, true, err
}

runningInAzure := resp.StatusCode < 400 || resp.StatusCode > 499

return nil, runningInAzure, errors.New(http.StatusText(resp.StatusCode))
}
114 changes: 114 additions & 0 deletions detectors/azure/azurevm/vm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package azurevm

import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
pyohannes marked this conversation as resolved.
Show resolved Hide resolved
)

func TestDetect(t *testing.T) {
type input struct {
jsonMetadata string
statusCode int
}
type expected struct {
resource *resource.Resource
err bool
}
type testCase struct {
input input
expected expected
}

testTable := []testCase{
{
input: input{
jsonMetadata: `{
"location": "us-west3",
"resourceId": "/subscriptions/sid/resourceGroups/rid/providers/pname/name",
"vmId": "43f65c49-8715-4639-88a9-be6d7eb749a5",
"name": "localhost-3",
"vmSize": "Standard_D2s_v3",
"osType": "linux",
"version": "6.5.0-26-generic"
}`,
statusCode: http.StatusOK,
},
expected: expected{
resource: resource.NewWithAttributes(semconv.SchemaURL, []attribute.KeyValue{
semconv.CloudProviderAzure,
semconv.CloudPlatformAzureVM,
semconv.CloudRegion("us-west3"),
semconv.CloudResourceID("/subscriptions/sid/resourceGroups/rid/providers/pname/name"),
semconv.HostID("43f65c49-8715-4639-88a9-be6d7eb749a5"),
semconv.HostName("localhost-3"),
semconv.HostType("Standard_D2s_v3"),
semconv.OSTypeKey.String("linux"),
semconv.OSVersion("6.5.0-26-generic"),
}...),
err: false,
},
},
{
input: input{
jsonMetadata: `{`,
statusCode: http.StatusOK,
},
expected: expected{
resource: nil,
err: true,
},
},
{
input: input{
jsonMetadata: "",
statusCode: http.StatusNotFound,
},
expected: expected{
resource: resource.Empty(),
err: false,
},
},
{
input: input{
jsonMetadata: "",
statusCode: http.StatusInternalServerError,
},
expected: expected{
resource: nil,
err: true,
},
},
}

for _, tCase := range testTable {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(tCase.input.statusCode)

if r.Header.Get("Metadata") == "True" {
fmt.Fprintf(w, tCase.input.jsonMetadata)
}
}))

detector := New()
detector.endpoint = svr.URL

azureResource, err := detector.Detect(context.Background())

svr.Close()

assert.Equal(t, err != nil, tCase.expected.err)
assert.Equal(t, tCase.expected.resource, azureResource)
}
}
1 change: 1 addition & 0 deletions versions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ module-sets:
modules:
- go.opentelemetry.io/contrib/bridges/prometheus
- go.opentelemetry.io/contrib/detectors/aws/lambda
- go.opentelemetry.io/contrib/detectors/azure/azurevm
pyohannes marked this conversation as resolved.
Show resolved Hide resolved
- go.opentelemetry.io/contrib/exporters/autoexport
- go.opentelemetry.io/contrib/propagators/autoprop
- go.opentelemetry.io/contrib/propagators/opencensus
Expand Down
Loading