Skip to content

Commit 53a5aea

Browse files
authored
Using google sdk to discover the project ID if it was not provided (#62)
* moving back the project ID to the main package, as, with the auto discovery, we want that to be defined at boottime, blocking the startup if needed, rather than doing it on each collector creation / handler. Simple project-id discovery logic using the gcloud sdk Signed-off-by: Daniel Caballero <[email protected]>
1 parent 770b1be commit 53a5aea

File tree

6 files changed

+189680
-23
lines changed

6 files changed

+189680
-23
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ If you are still using the legacy [Access scopes][access-scopes], the `https://w
6363

6464
| Flag / Environment Variable | Required | Default | Description |
6565
| --------------------------- | -------- | ------- | ----------- |
66-
| `google.project-id`<br />`STACKDRIVER_EXPORTER_GOOGLE_PROJECT_ID` | Yes | | Google Project ID |
66+
| `google.project-id`<br />`STACKDRIVER_EXPORTER_GOOGLE_PROJECT_ID` | No | GCloud SDK autodiscovery | Google Project ID |
6767
| `monitoring.metrics-type-prefixes`<br />`STACKDRIVER_EXPORTER_MONITORING_METRICS_TYPE_PREFIXES` | Yes | | Comma separated Google Stackdriver Monitoring Metric Type prefixes (see [example][metrics-prefix-example] and [available metrics][metrics-list]) |
6868
| `monitoring.metrics-interval`<br />`STACKDRIVER_EXPORTER_MONITORING_METRICS_INTERVAL` | No | `5m` | Metric's timestamp interval to request from the Google Stackdriver Monitoring Metrics API. Only the most recent data point is used |
6969
| `monitoring.metrics-offset`<br />`STACKDRIVER_EXPORTER_MONITORING_METRICS_OFFSET` | No | `0s` | Offset (into the past) for the metric's timestamp interval to request from the Google Stackdriver Monitoring Metrics API, to handle latency in published metrics |

collectors/monitoring_collector.go

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@ import (
3333
)
3434

3535
var (
36-
projectID = kingpin.Flag(
37-
"google.project-id", "Google Project ID ($STACKDRIVER_EXPORTER_GOOGLE_PROJECT_ID).",
38-
).Envar("STACKDRIVER_EXPORTER_GOOGLE_PROJECT_ID").Required().String()
39-
4036
monitoringMetricsTypePrefixes = kingpin.Flag(
4137
"monitoring.metrics-type-prefixes", "Comma separated Google Stackdriver Monitoring Metric Type prefixes ($STACKDRIVER_EXPORTER_MONITORING_METRICS_TYPE_PREFIXES).",
4238
).Envar("STACKDRIVER_EXPORTER_MONITORING_METRICS_TYPE_PREFIXES").Required().String()
@@ -75,11 +71,7 @@ type MonitoringCollector struct {
7571
logger log.Logger
7672
}
7773

78-
func NewMonitoringCollector(monitoringService *monitoring.Service, filters map[string]bool, logger log.Logger) (*MonitoringCollector, error) {
79-
if *projectID == "" {
80-
return nil, errors.New("Flag `google.project-id` is required")
81-
}
82-
74+
func NewMonitoringCollector(projectID string, monitoringService *monitoring.Service, filters map[string]bool, logger log.Logger) (*MonitoringCollector, error) {
8375
if *monitoringMetricsTypePrefixes == "" {
8476
return nil, errors.New("Flag `monitoring.metrics-type-prefixes` is required")
8577
}
@@ -90,7 +82,7 @@ func NewMonitoringCollector(monitoringService *monitoring.Service, filters map[s
9082
Subsystem: "monitoring",
9183
Name: "api_calls_total",
9284
Help: "Total number of Google Stackdriver Monitoring API calls made.",
93-
ConstLabels: prometheus.Labels{"project_id": *projectID},
85+
ConstLabels: prometheus.Labels{"project_id": projectID},
9486
},
9587
)
9688

@@ -100,7 +92,7 @@ func NewMonitoringCollector(monitoringService *monitoring.Service, filters map[s
10092
Subsystem: "monitoring",
10193
Name: "scrapes_total",
10294
Help: "Total number of Google Stackdriver Monitoring metrics scrapes.",
103-
ConstLabels: prometheus.Labels{"project_id": *projectID},
95+
ConstLabels: prometheus.Labels{"project_id": projectID},
10496
},
10597
)
10698

@@ -110,7 +102,7 @@ func NewMonitoringCollector(monitoringService *monitoring.Service, filters map[s
110102
Subsystem: "monitoring",
111103
Name: "scrape_errors_total",
112104
Help: "Total number of Google Stackdriver Monitoring metrics scrape errors.",
113-
ConstLabels: prometheus.Labels{"project_id": *projectID},
105+
ConstLabels: prometheus.Labels{"project_id": projectID},
114106
},
115107
)
116108

@@ -120,7 +112,7 @@ func NewMonitoringCollector(monitoringService *monitoring.Service, filters map[s
120112
Subsystem: "monitoring",
121113
Name: "last_scrape_error",
122114
Help: "Whether the last metrics scrape from Google Stackdriver Monitoring resulted in an error (1 for error, 0 for success).",
123-
ConstLabels: prometheus.Labels{"project_id": *projectID},
115+
ConstLabels: prometheus.Labels{"project_id": projectID},
124116
},
125117
)
126118

@@ -130,7 +122,7 @@ func NewMonitoringCollector(monitoringService *monitoring.Service, filters map[s
130122
Subsystem: "monitoring",
131123
Name: "last_scrape_timestamp",
132124
Help: "Number of seconds since 1970 since last metrics scrape from Google Stackdriver Monitoring.",
133-
ConstLabels: prometheus.Labels{"project_id": *projectID},
125+
ConstLabels: prometheus.Labels{"project_id": projectID},
134126
},
135127
)
136128

@@ -140,7 +132,7 @@ func NewMonitoringCollector(monitoringService *monitoring.Service, filters map[s
140132
Subsystem: "monitoring",
141133
Name: "last_scrape_duration_seconds",
142134
Help: "Duration of the last metrics scrape from Google Stackdriver Monitoring.",
143-
ConstLabels: prometheus.Labels{"project_id": *projectID},
135+
ConstLabels: prometheus.Labels{"project_id": projectID},
144136
},
145137
)
146138

@@ -156,7 +148,7 @@ func NewMonitoringCollector(monitoringService *monitoring.Service, filters map[s
156148
}
157149

158150
monitoringCollector := &MonitoringCollector{
159-
projectID: *projectID,
151+
projectID: projectID,
160152
metricsTypePrefixes: filteredPrefixes,
161153
metricsInterval: *monitoringMetricsInterval,
162154
metricsOffset: *monitoringMetricsOffset,

stackdriver_exporter.go

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/prometheus/common/version"
2929
"golang.org/x/net/context"
3030
"golang.org/x/oauth2/google"
31+
"google.golang.org/api/compute/v1"
3132
"google.golang.org/api/monitoring/v3"
3233
"google.golang.org/api/option"
3334
"gopkg.in/alecthomas/kingpin.v2"
@@ -44,6 +45,10 @@ var (
4445
"web.telemetry-path", "Path under which to expose Prometheus metrics ($STACKDRIVER_EXPORTER_WEB_TELEMETRY_PATH).",
4546
).Envar("STACKDRIVER_EXPORTER_WEB_TELEMETRY_PATH").Default("/metrics").String()
4647

48+
projectID = kingpin.Flag(
49+
"google.project-id", "Google Project ID ($STACKDRIVER_EXPORTER_GOOGLE_PROJECT_ID).",
50+
).Envar("STACKDRIVER_EXPORTER_GOOGLE_PROJECT_ID").String()
51+
4752
stackdriverMaxRetries = kingpin.Flag(
4853
"stackdriver.max-retries", "Max number of retries that should be attempted on 503 errors from stackdriver. ($STACKDRIVER_EXPORTER_MAX_RETRIES)",
4954
).Envar("STACKDRIVER_EXPORTER_MAX_RETRIES").Default("0").Int()
@@ -69,9 +74,18 @@ func init() {
6974
prometheus.MustRegister(version.NewCollector("stackdriver_exporter"))
7075
}
7176

72-
func createMonitoringService() (*monitoring.Service, error) {
73-
ctx := context.Background()
77+
func getDefaultGCPProject(ctx context.Context) (*string, error) {
78+
credentials, err := google.FindDefaultCredentials(ctx, compute.ComputeScope)
79+
if err != nil {
80+
return nil, err
81+
}
82+
if credentials.ProjectID == "" {
83+
return nil, fmt.Errorf("unable to identify the gcloud project. Got empty string")
84+
}
85+
return &credentials.ProjectID, nil
86+
}
7487

88+
func createMonitoringService(ctx context.Context) (*monitoring.Service, error) {
7589
googleClient, err := google.DefaultClient(ctx, monitoring.MonitoringReadScope)
7690
if err != nil {
7791
return nil, fmt.Errorf("Error creating Google client: %v", err)
@@ -94,7 +108,7 @@ func createMonitoringService() (*monitoring.Service, error) {
94108
return monitoringService, nil
95109
}
96110

97-
func newHandler(m *monitoring.Service, logger log.Logger) http.HandlerFunc {
111+
func newHandler(projectID string, m *monitoring.Service, logger log.Logger) http.HandlerFunc {
98112
return func(w http.ResponseWriter, r *http.Request) {
99113
collectParams := r.URL.Query()["collect"]
100114

@@ -104,7 +118,7 @@ func newHandler(m *monitoring.Service, logger log.Logger) http.HandlerFunc {
104118
filters[param] = true
105119
}
106120

107-
monitoringCollector, err := collectors.NewMonitoringCollector(m, filters, logger)
121+
monitoringCollector, err := collectors.NewMonitoringCollector(projectID, m, filters, logger)
108122
if err != nil {
109123
level.Error(logger).Log("err", err)
110124
os.Exit(1)
@@ -133,16 +147,28 @@ func main() {
133147

134148
logger := promlog.New(promlogConfig)
135149

150+
ctx := context.Background()
151+
if *projectID == "" {
152+
level.Info(logger).Log("msg", "no projectID was provided. Trying to discover it")
153+
var err error
154+
projectID, err = getDefaultGCPProject(ctx)
155+
if err != nil {
156+
level.Error(logger).Log("msg", "no explicit projectID and error trying to discover default GCloud project", "err", err)
157+
os.Exit(1)
158+
}
159+
}
160+
136161
level.Info(logger).Log("msg", "Starting stackdriver_exporter", "version", version.Info())
137162
level.Info(logger).Log("msg", "Build context", "build_context", version.BuildContext())
163+
level.Info(logger).Log("msg", "Using Google Cloud Project ID", "projectID", *projectID)
138164

139-
monitoringService, err := createMonitoringService()
165+
monitoringService, err := createMonitoringService(ctx)
140166
if err != nil {
141167
level.Error(logger).Log("msg", "failed to create monitoring service", "err", err)
142168
os.Exit(1)
143169
}
144170

145-
handlerFunc := newHandler(monitoringService, logger)
171+
handlerFunc := newHandler(*projectID, monitoringService, logger)
146172

147173
http.Handle(*metricsPath, promhttp.InstrumentMetricHandler(prometheus.DefaultRegisterer, handlerFunc))
148174
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

0 commit comments

Comments
 (0)