Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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

- Add support for the `aws.ec2` resource detector in `go.opentelemetry.io/contrib/otelconf/x`. (#9139)
- Add Bare Metal Solution (BMS) detection, GCE Managed Instance Group (MIG) attributes, Cloud Run Worker Pool parity, and GKE hostname enrichment to `go.opentelemetry.io/contrib/detectors/gcp`. (#9462)

### Fixed

Expand Down
3 changes: 2 additions & 1 deletion detectors/gcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The GCP resource detector supports detecting resources on:
* Cloud Run
* Cloud Run jobs
* Cloud Functions
* Bare Metal Solution (BMS)
Comment thread
dashpole marked this conversation as resolved.

## Usage

Expand Down Expand Up @@ -56,8 +57,8 @@ env:
value: my-container-name
- name: OTEL_RESOURCE_ATTRIBUTES
value: k8s.pod.name=$(POD_NAME),k8s.namespace.name=$(NAMESPACE_NAME),k8s.container.name=$(CONTAINER_NAME)

```

To have a detector unpack the `OTEL_RESOURCE_ATTRIBUTES` envvar, use the `WithFromEnv` option:

```golang
Expand Down
37 changes: 34 additions & 3 deletions detectors/gcp/detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
// * Google App Engine (GAE).
// * Cloud Run.
// * Cloud Functions.
// * Bare Metal Solution (BMS).
func NewDetector() resource.Detector {
return &detector{detector: gcp.NewDetector()}
}
Expand All @@ -31,22 +32,33 @@ type detector struct {
}

// Detect detects associated resources when running on GCE, GKE, GAE,
// Cloud Run, and Cloud functions.
// Cloud Run, Cloud Functions, and Bare Metal Solution.
func (d *detector) Detect(context.Context) (*resource.Resource, error) {
cp := d.detector.CloudPlatform()
if cp == gcp.BareMetalSolution {
b := &resourceBuilder{}
b.attrs = append(b.attrs, semconv.CloudProviderGCP, semconv.CloudPlatformGCPBareMetalSolution)
b.add(semconv.CloudAccountIDKey, d.detector.BareMetalSolutionProjectID)
b.add(semconv.HostNameKey, d.detector.BareMetalSolutionInstanceID)
b.add(semconv.CloudRegionKey, d.detector.BareMetalSolutionCloudRegion)
return b.build()
}

if !metadata.OnGCE() {
return nil, nil
}
b := &resourceBuilder{}
b.attrs = append(b.attrs, semconv.CloudProviderGCP)
b.add(semconv.CloudAccountIDKey, d.detector.ProjectID)

switch d.detector.CloudPlatform() {
switch cp {
case gcp.GKE:
b.attrs = append(b.attrs, semconv.CloudPlatformGCPKubernetesEngine)
b.addZoneOrRegion(d.detector.GKEAvailabilityZoneOrRegion)
b.add(semconv.K8SClusterNameKey, d.detector.GKEClusterName)
b.add(semconv.HostIDKey, d.detector.GKEHostID)
case gcp.CloudRun:
b.add(semconv.HostNameKey, d.detector.GCEHostName)
case gcp.CloudRun, gcp.CloudRunWorkerPool:
b.attrs = append(b.attrs, semconv.CloudPlatformGCPCloudRun)
b.add(semconv.FaaSNameKey, d.detector.FaaSName)
b.add(semconv.FaaSVersionKey, d.detector.FaaSVersion)
Expand Down Expand Up @@ -86,6 +98,7 @@ func (d *detector) Detect(context.Context) (*resource.Resource, error) {
b.add(semconv.HostNameKey, d.detector.GCEHostName)
b.add(semconv.GCPGCEInstanceNameKey, d.detector.GCEInstanceName)
b.add(semconv.GCPGCEInstanceHostnameKey, d.detector.GCEInstanceHostname)
b.addManagedInstanceGroup(d.detector.GCEManagedInstanceGroup)
default:
// We don't support this platform yet, so just return with what we have
}
Expand Down Expand Up @@ -147,6 +160,24 @@ func (r *resourceBuilder) addZoneOrRegion(detect func() (string, gcp.LocationTyp
}
}

func (r *resourceBuilder) addManagedInstanceGroup(detect func() (gcp.ManagedInstanceGroup, error)) {
if mig, err := detect(); err == nil {
if mig.Name != "" {
r.attrs = append(r.attrs, semconv.GCPGCEInstanceGroupManagerName(mig.Name))
switch mig.Type {
case gcp.Zone:
r.attrs = append(r.attrs, semconv.GCPGCEInstanceGroupManagerZone(mig.Location))
case gcp.Region:
r.attrs = append(r.attrs, semconv.GCPGCEInstanceGroupManagerRegion(mig.Location))
default:
r.errs = append(r.errs, fmt.Errorf("managed instance group location must be zone or region. Got %v", mig.Type))
}
}
} else {
r.errs = append(r.errs, err)
}
}

func (r *resourceBuilder) build() (*resource.Resource, error) {
var err error
if len(r.errs) > 0 {
Expand Down
231 changes: 204 additions & 27 deletions detectors/gcp/detector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func TestDetect(t *testing.T) {
projectID: "my-project",
cloudPlatform: gcp.GKE,
gkeHostID: "1472385723456792345",
gceHostName: "my-gke-node-1234",
gkeClusterName: "my-cluster",
gkeAvailabilityZone: "us-central1-c",
}},
Expand All @@ -40,6 +41,7 @@ func TestDetect(t *testing.T) {
semconv.K8SClusterName("my-cluster"),
semconv.CloudAvailabilityZone("us-central1-c"),
semconv.HostID("1472385723456792345"),
semconv.HostName("my-gke-node-1234"),
),
},
{
Expand All @@ -48,6 +50,7 @@ func TestDetect(t *testing.T) {
projectID: "my-project",
cloudPlatform: gcp.GKE,
gkeHostID: "1472385723456792345",
gceHostName: "my-gke-node-1234",
gkeClusterName: "my-cluster",
gkeRegion: "us-central1",
}},
Expand All @@ -59,10 +62,11 @@ func TestDetect(t *testing.T) {
semconv.K8SClusterName("my-cluster"),
semconv.CloudRegion("us-central1"),
semconv.HostID("1472385723456792345"),
semconv.HostName("my-gke-node-1234"),
),
},
{
desc: "GCE",
desc: "GCE without MIG",
detector: &detector{detector: &fakeGCPDetector{
projectID: "my-project",
cloudPlatform: gcp.GCE,
Expand Down Expand Up @@ -222,6 +226,147 @@ func TestDetect(t *testing.T) {
semconv.FaaSInstance("1472385723456792345"),
),
},
{
desc: "GCE with MIG",
detector: &detector{detector: &fakeGCPDetector{
projectID: "my-project",
cloudPlatform: gcp.GCE,
gceHostID: "1472385723456792345",
gceHostName: "my-gke-node-1234",
gceHostType: "n1-standard1",
gceAvailabilityZone: "us-central1-c",
gceRegion: "us-central1",
gcpGceInstanceName: "my-gke-node-1234",
gcpGceInstanceHostname: "hostname",
gcpGceManagedInstanceGroup: gcp.ManagedInstanceGroup{
Name: "my-mig",
Location: "us-central1",
Type: gcp.Region,
},
}},
expectedResource: resource.NewWithAttributes(
semconv.SchemaURL,
semconv.CloudProviderGCP,
semconv.CloudAccountID("my-project"),
semconv.CloudPlatformGCPComputeEngine,
semconv.HostID("1472385723456792345"),
semconv.HostName("my-gke-node-1234"),
semconv.GCPGCEInstanceNameKey.String("my-gke-node-1234"),
semconv.GCPGCEInstanceHostnameKey.String("hostname"),
semconv.HostType("n1-standard1"),
semconv.CloudRegion("us-central1"),
semconv.CloudAvailabilityZone("us-central1-c"),
semconv.GCPGCEInstanceGroupManagerName("my-mig"),
semconv.GCPGCEInstanceGroupManagerRegion("us-central1"),
),
},
{
desc: "GCE with zonal MIG",
detector: &detector{detector: &fakeGCPDetector{
projectID: "my-project",
cloudPlatform: gcp.GCE,
gceHostID: "1472385723456792345",
gceHostName: "my-gke-node-1234",
gceHostType: "n1-standard1",
gceAvailabilityZone: "us-central1-c",
gceRegion: "us-central1",
gcpGceInstanceName: "my-gke-node-1234",
gcpGceInstanceHostname: "hostname",
gcpGceManagedInstanceGroup: gcp.ManagedInstanceGroup{
Name: "my-mig",
Location: "us-central1-c",
Type: gcp.Zone,
},
}},
expectedResource: resource.NewWithAttributes(
semconv.SchemaURL,
semconv.CloudProviderGCP,
semconv.CloudAccountID("my-project"),
semconv.CloudPlatformGCPComputeEngine,
semconv.HostID("1472385723456792345"),
semconv.HostName("my-gke-node-1234"),
semconv.GCPGCEInstanceNameKey.String("my-gke-node-1234"),
semconv.GCPGCEInstanceHostnameKey.String("hostname"),
semconv.HostType("n1-standard1"),
semconv.CloudRegion("us-central1"),
semconv.CloudAvailabilityZone("us-central1-c"),
semconv.GCPGCEInstanceGroupManagerName("my-mig"),
semconv.GCPGCEInstanceGroupManagerZone("us-central1-c"),
),
},
{
desc: "GCE with MIG invalid type",
detector: &detector{detector: &fakeGCPDetector{
projectID: "my-project",
cloudPlatform: gcp.GCE,
gceHostID: "1472385723456792345",
gceHostName: "my-gke-node-1234",
gceHostType: "n1-standard1",
gceAvailabilityZone: "us-central1-c",
gceRegion: "us-central1",
gcpGceInstanceName: "my-gke-node-1234",
gcpGceInstanceHostname: "hostname",
gcpGceManagedInstanceGroup: gcp.ManagedInstanceGroup{
Name: "my-mig",
Location: "us-central1",
Type: gcp.LocationType(99),
},
}},
expectErr: true,
expectedResource: resource.NewWithAttributes(
semconv.SchemaURL,
semconv.CloudProviderGCP,
semconv.CloudAccountID("my-project"),
semconv.CloudPlatformGCPComputeEngine,
semconv.HostID("1472385723456792345"),
semconv.HostName("my-gke-node-1234"),
semconv.GCPGCEInstanceNameKey.String("my-gke-node-1234"),
semconv.GCPGCEInstanceHostnameKey.String("hostname"),
semconv.HostType("n1-standard1"),
semconv.CloudRegion("us-central1"),
semconv.CloudAvailabilityZone("us-central1-c"),
semconv.GCPGCEInstanceGroupManagerName("my-mig"),
),
},
{
desc: "Cloud Run Worker Pool",
detector: &detector{detector: &fakeGCPDetector{
projectID: "my-project",
cloudPlatform: gcp.CloudRunWorkerPool,
faaSID: "1472385723456792345",
faaSCloudRegion: "us-central1",
faaSName: "my-service",
faaSVersion: "123456",
}},
expectedResource: resource.NewWithAttributes(
semconv.SchemaURL,
semconv.CloudProviderGCP,
semconv.CloudAccountID("my-project"),
semconv.CloudPlatformGCPCloudRun,
semconv.CloudRegion("us-central1"),
semconv.FaaSName("my-service"),
semconv.FaaSVersion("123456"),
semconv.FaaSInstance("1472385723456792345"),
),
},
{
desc: "Bare Metal Solution",
detector: &detector{detector: &fakeGCPDetector{
projectID: "my-project",
cloudPlatform: gcp.BareMetalSolution,
gcpBareMetalSolutionCloudRegion: "us-central1",
gcpBareMetalSolutionInstanceID: "1472385723456792345",
gcpBareMetalSolutionProjectID: "my-project",
}},
expectedResource: resource.NewWithAttributes(
semconv.SchemaURL,
semconv.CloudProviderGCP,
semconv.CloudPlatformGCPBareMetalSolution,
semconv.CloudAccountID("my-project"),
semconv.HostName("1472385723456792345"),
semconv.CloudRegion("us-central1"),
),
},
{
desc: "Unknown Platform",
detector: &detector{detector: &fakeGCPDetector{
Expand Down Expand Up @@ -260,32 +405,36 @@ func TestDetect(t *testing.T) {

// fakeGCPDetector implements gcpDetector and uses fake values.
type fakeGCPDetector struct {
err error
projectID string
cloudPlatform gcp.Platform
gkeAvailabilityZone string
gkeRegion string
gkeClusterName string
gkeHostID string
gkeHostName string
faaSName string
faaSVersion string
faaSID string
faaSCloudRegion string
appEngineAvailabilityZone string
appEngineRegion string
appEngineServiceName string
appEngineServiceVersion string
appEngineServiceInstance string
gceAvailabilityZone string
gceRegion string
gceHostType string
gceHostID string
gceHostName string
gcpGceInstanceName string
gcpGceInstanceHostname string
cloudRunJobExecution string
cloudRunJobTaskIndex string
err error
projectID string
cloudPlatform gcp.Platform
gkeAvailabilityZone string
gkeRegion string
gkeClusterName string
gkeHostID string
gkeHostName string
faaSName string
faaSVersion string
faaSID string
faaSCloudRegion string
appEngineAvailabilityZone string
appEngineRegion string
appEngineServiceName string
appEngineServiceVersion string
appEngineServiceInstance string
gceAvailabilityZone string
gceRegion string
gceHostType string
gceHostID string
gceHostName string
gcpGceInstanceName string
gcpGceInstanceHostname string
gcpGceManagedInstanceGroup gcp.ManagedInstanceGroup
gcpBareMetalSolutionCloudRegion string
gcpBareMetalSolutionInstanceID string
gcpBareMetalSolutionProjectID string
cloudRunJobExecution string
cloudRunJobTaskIndex string
}

func (f *fakeGCPDetector) ProjectID() (string, error) {
Expand Down Expand Up @@ -455,3 +604,31 @@ func (f *fakeGCPDetector) CloudRunJobTaskIndex() (string, error) {
}
return f.cloudRunJobTaskIndex, nil
}

func (f *fakeGCPDetector) GCEManagedInstanceGroup() (gcp.ManagedInstanceGroup, error) {
if f.err != nil {
return gcp.ManagedInstanceGroup{}, f.err
}
return f.gcpGceManagedInstanceGroup, nil
}

func (f *fakeGCPDetector) BareMetalSolutionInstanceID() (string, error) {
if f.err != nil {
return "", f.err
}
return f.gcpBareMetalSolutionInstanceID, nil
}

func (f *fakeGCPDetector) BareMetalSolutionCloudRegion() (string, error) {
if f.err != nil {
return "", f.err
}
return f.gcpBareMetalSolutionCloudRegion, nil
}

func (f *fakeGCPDetector) BareMetalSolutionProjectID() (string, error) {
if f.err != nil {
return "", f.err
}
return f.gcpBareMetalSolutionProjectID, nil
}
4 changes: 4 additions & 0 deletions detectors/gcp/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,8 @@ type gcpDetector interface {
GCEInstanceName() (string, error)
CloudRunJobExecution() (string, error)
CloudRunJobTaskIndex() (string, error)
GCEManagedInstanceGroup() (gcp.ManagedInstanceGroup, error)
BareMetalSolutionInstanceID() (string, error)
BareMetalSolutionCloudRegion() (string, error)
BareMetalSolutionProjectID() (string, error)
}
Loading