sandbox: correct stackdriver labels
The generic_task target has a required label format that was not being
met. Moves metadata fetching into a helper function.
Removes call to view.RegisterExporter for StackDriver exporter, which
was unncessary.
Updates golang/go#25224
Updates golang/go#38530
Change-Id: Ib009f5ce906f5b9479cdda8c7e8322d06e3036e4
Reviewed-on: https://go-review.googlesource.com/c/playground/+/229958
Run-TryBot: Alexander Rakoczy <alex@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Carlos Amedee <carlos@golang.org>
diff --git a/sandbox/metrics.go b/sandbox/metrics.go
index 3d80066..82b4329 100644
--- a/sandbox/metrics.go
+++ b/sandbox/metrics.go
@@ -5,8 +5,10 @@
package main
import (
+ "errors"
"fmt"
"net/http"
+ "path"
"time"
"cloud.google.com/go/compute/metadata"
@@ -126,26 +128,14 @@
if err != nil {
return nil, err
}
- zone, err := metadata.Zone()
- if err != nil {
- return nil, err
- }
- iname, err := metadata.InstanceName()
+ gr, err := gceResource("go-playground-sandbox")
if err != nil {
return nil, err
}
sd, err := stackdriver.NewExporter(stackdriver.Options{
- ProjectID: projID,
- MonitoredResource: (*monitoredResource)(&mrpb.MonitoredResource{
- Type: "generic_task",
- Labels: map[string]string{
- "instance_id": iname,
- "job": "go-playground-sandbox",
- "project_id": projID,
- "zone": zone,
- },
- }),
+ ProjectID: projID,
+ MonitoredResource: gr,
ReportingInterval: time.Minute, // Minimum interval for stackdriver is 1 minute.
})
if err != nil {
@@ -154,7 +144,6 @@
// Minimum interval for stackdriver is 1 minute.
view.SetReportingPeriod(time.Minute)
- view.RegisterExporter(sd)
// Start the metrics exporter.
if err := sd.StartMetricsExporter(); err != nil {
return nil, err
@@ -163,6 +152,7 @@
return &metricService{sdExporter: sd}, nil
}
+// metricService controls metric exporters.
type metricService struct {
sdExporter *stackdriver.Exporter
pExporter *prometheus.Exporter
@@ -176,6 +166,7 @@
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
}
+// Stop flushes metrics and stops exporting. Stop should be called before exiting.
func (m *metricService) Stop() {
if sde := m.sdExporter; sde != nil {
// Flush any unsent data before exiting.
@@ -192,3 +183,58 @@
func (r *monitoredResource) MonitoredResource() (resType string, labels map[string]string) {
return r.Type, r.Labels
}
+
+// gceResource populates a monitoredResource with GCE Metadata.
+//
+// The returned monitoredResource will have the type set to "generic_task".
+func gceResource(jobName string) (*monitoredResource, error) {
+ projID, err := metadata.ProjectID()
+ if err != nil {
+ return nil, err
+ }
+ zone, err := metadata.Zone()
+ if err != nil {
+ return nil, err
+ }
+ iname, err := metadata.InstanceName()
+ if err != nil {
+ return nil, err
+ }
+ igName, err := instanceGroupName()
+ if err != nil {
+ return nil, err
+ } else if igName == "" {
+ igName = projID
+ }
+
+ return (*monitoredResource)(&mrpb.MonitoredResource{
+ Type: "generic_task", // See: https://cloud.google.com/monitoring/api/resources#tag_generic_task
+ Labels: map[string]string{
+ "project_id": projID,
+ "location": zone,
+ "namespace": igName,
+ "job": jobName,
+ "task_id": iname,
+ },
+ }), nil
+}
+
+// instanceGroupName fetches the instanceGroupName from the instance metadata.
+//
+// The instance group manager applies a custom "created-by" attribute to the instance, which is not part of the
+// metadata package API, and must be queried separately.
+//
+// An empty string will be returned if a metadata.NotDefinedError is returned when fetching metadata.
+// An error will be returned if other errors occur when fetching metadata.
+func instanceGroupName() (string, error) {
+ ig, err := metadata.InstanceAttributeValue("created-by")
+ if nde := metadata.NotDefinedError(""); err != nil && !errors.As(err, &nde) {
+ return "", err
+ }
+ if ig == "" {
+ return "", nil
+ }
+ // "created-by" format: "projects/{{InstanceID}}/zones/{{Zone}}/instanceGroupManagers/{{Instance Group Name}}
+ ig = path.Base(ig)
+ return ig, err
+}