extract part of monitoring lib to x/lib/gcm.
The main purpose is to let allocatord to use those functions
to retrieve data from GCM to render dashboard.
MultiPart: 2/2
Change-Id: I2aed93cb8f93a9334280bf4bd909d4d72f7deba8
diff --git a/gcm/.api b/gcm/.api
new file mode 100644
index 0000000..217fbb0
--- /dev/null
+++ b/gcm/.api
@@ -0,0 +1,3 @@
+pkg gcm, func Authenticate(string) (*cloudmonitoring.Service, error)
+pkg gcm, func GetMetric(string, string) (*cloudmonitoring.MetricDescriptor, error)
+pkg gcm, func GetSortedMetricNames() []string
diff --git a/gcm/.godepcop b/gcm/.godepcop
new file mode 100644
index 0000000..f3544ad
--- /dev/null
+++ b/gcm/.godepcop
@@ -0,0 +1,6 @@
+<godepcop>
+ <pkg allow="golang.org/x/oauth2/..."/>
+ <pkg allow="golang.org/x/net/..."/>
+ <pkg allow="google.golang.org/api/..."/>
+ <pkg allow="google.golang.org/cloud/..."/>
+</godepcop>
diff --git a/gcm/gcm.go b/gcm/gcm.go
new file mode 100644
index 0000000..40a0500
--- /dev/null
+++ b/gcm/gcm.go
@@ -0,0 +1,206 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gcm
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "sort"
+
+ "golang.org/x/oauth2"
+ "golang.org/x/oauth2/google"
+ cloudmonitoring "google.golang.org/api/monitoring/v3"
+)
+
+const (
+ customMetricPrefix = "custom.googleapis.com"
+)
+
+type labelData struct {
+ key string
+ description string
+}
+
+var aggLabelData = []labelData{
+ labelData{
+ key: "aggregation",
+ description: "The aggregation type (min, max, avg, sum, count)",
+ },
+}
+
+// customMetricDescriptors is a map from metric's short names to their
+// MetricDescriptor definitions.
+var customMetricDescriptors = map[string]*cloudmonitoring.MetricDescriptor{
+ // Custom metrics for recording stats of cloud syncbase instances.
+ "cloud-syncbase": createMetric("cloud-syncbase", "Stats of cloud syncbase instances.", "double", false, []labelData{
+ labelData{
+ key: "mounted_name",
+ description: "The relative mounted name of the instance",
+ },
+ }),
+ "cloud-syncbase-agg": createMetric("cloud-syncbase-agg", "The aggregated stats of cloud syncbase instances.", "double", false, aggLabelData),
+
+ // Custom metrics for recording check latency and its aggregation
+ // of vanadium production services.
+ "service-latency": createMetric("service/latency", "The check latency (ms) of vanadium production services.", "double", true, nil),
+ "service-latency-agg": createMetric("service/latency-agg", "The aggregated check latency (ms) of vanadium production services.", "double", false, aggLabelData),
+
+ // Custom metric for recording per-method rpc latency and its aggregation
+ // for a service.
+ "service-permethod-latency": createMetric("service/latency/method", "Service latency (ms) per method.", "double", true, []labelData{
+ labelData{
+ key: "method_name",
+ description: "The method name",
+ },
+ }),
+ "service-permethod-latency-agg": createMetric("service/latency/method-agg", "Aggregated service latency (ms) per method.", "double", false, []labelData{
+ labelData{
+ key: "method_name",
+ description: "The method name",
+ },
+ aggLabelData[0],
+ }),
+
+ // Custom metric for recording various counters and their aggregations
+ // of vanadium production services.
+ "service-counters": createMetric("service/counters", "Various counters of vanadium production services.", "double", true, nil),
+ "service-counters-agg": createMetric("service/counters-agg", "Aggregated counters of vanadium production services.", "double", false, aggLabelData),
+
+ // Custom metric for recording service metadata and its aggregation
+ // of vanadium production services.
+ "service-metadata": createMetric("service/metadata", "Various metadata of vanadium production services.", "double", true, []labelData{
+ labelData{
+ key: "metadata_name",
+ description: "The metadata name",
+ },
+ }),
+ "service-metadata-agg": createMetric("service/metadata-agg", "Aggregated metadata of vanadium production services.", "double", false, []labelData{
+ labelData{
+ key: "metadata_name",
+ description: "The metadata name",
+ },
+ aggLabelData[0],
+ }),
+
+ // Custom metric for recording total rpc qps and its aggregation for a service.
+ "service-qps-total": createMetric("service/qps/total", "Total service QPS.", "double", true, nil),
+ "service-qps-total-agg": createMetric("service/qps/total-agg", "Aggregated total service QPS.", "double", false, aggLabelData),
+
+ // Custom metric for recording per-method rpc qps for a service.
+ "service-qps-method": createMetric("service/qps/method", "Service QPS per method.", "double", true, []labelData{
+ labelData{
+ key: "method_name",
+ description: "The method name",
+ },
+ }),
+ "service-qps-method-agg": createMetric("service/qps/method-agg", "Aggregated service QPS per method.", "double", false, []labelData{
+ labelData{
+ key: "method_name",
+ description: "The method name",
+ },
+ aggLabelData[0],
+ }),
+
+ // Custom metric for recording gce instance stats.
+ "gce-instance": createMetric("gce-instance/stats", "Various stats for GCE instances.", "double", true, nil),
+
+ // Custom metric for recording nginx stats.
+ "nginx": createMetric("nginx/stats", "Various stats for Nginx server.", "double", true, nil),
+
+ // Custom metric for rpc load tests.
+ "rpc-load-test": createMetric("rpc-load-test", "Results of rpc load test.", "double", false, nil),
+
+ // Custom metric for recording jenkins related data.
+ "jenkins": createMetric("jenkins", "Jenkins related data.", "double", false, nil),
+}
+
+func createMetric(metricType, description, valueType string, includeGCELabels bool, extraLabels []labelData) *cloudmonitoring.MetricDescriptor {
+ labels := []*cloudmonitoring.LabelDescriptor{}
+ if includeGCELabels {
+ labels = append(labels, &cloudmonitoring.LabelDescriptor{
+ Key: "gce_instance",
+ Description: "The name of the GCE instance associated with this metric.",
+ ValueType: "string",
+ }, &cloudmonitoring.LabelDescriptor{
+ Key: "gce_zone",
+ Description: "The zone of the GCE instance associated with this metric.",
+ ValueType: "string",
+ })
+ }
+ labels = append(labels, &cloudmonitoring.LabelDescriptor{
+ Key: "metric_name",
+ Description: "The name of the metric.",
+ ValueType: "string",
+ })
+ if extraLabels != nil {
+ for _, data := range extraLabels {
+ labels = append(labels, &cloudmonitoring.LabelDescriptor{
+ Key: fmt.Sprintf("%s", data.key),
+ Description: data.description,
+ ValueType: "string",
+ })
+ }
+ }
+
+ return &cloudmonitoring.MetricDescriptor{
+ Type: fmt.Sprintf("%s/vanadium/%s", customMetricPrefix, metricType),
+ Description: description,
+ MetricKind: "gauge",
+ ValueType: valueType,
+ Labels: labels,
+ }
+}
+
+// GetMetric gets the custom metric descriptor with the given name and project.
+func GetMetric(name, project string) (*cloudmonitoring.MetricDescriptor, error) {
+ md, ok := customMetricDescriptors[name]
+ if !ok {
+ return nil, fmt.Errorf("metric %q doesn't exist", name)
+ }
+ md.Name = fmt.Sprintf("projects/%s/metricDescriptors/%s", project, md.Type)
+ return md, nil
+}
+
+// GetSortedMetricNames gets the sorted metric names.
+func GetSortedMetricNames() []string {
+ names := []string{}
+ for n := range customMetricDescriptors {
+ names = append(names, n)
+ }
+ sort.Strings(names)
+ return names
+}
+
+func createClient(keyFilePath string) (*http.Client, error) {
+ if len(keyFilePath) > 0 {
+ data, err := ioutil.ReadFile(keyFilePath)
+ if err != nil {
+ return nil, err
+ }
+ conf, err := google.JWTConfigFromJSON(data, cloudmonitoring.MonitoringScope)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create JWT config file: %v", err)
+ }
+ return conf.Client(oauth2.NoContext), nil
+ }
+
+ return google.DefaultClient(oauth2.NoContext, cloudmonitoring.MonitoringScope)
+}
+
+// Authenticate authenticates with the given JSON credentials file (or the
+// default client if the file is not provided). If successful, it returns a
+// service object that can be used in GCM API calls.
+func Authenticate(keyFilePath string) (*cloudmonitoring.Service, error) {
+ c, err := createClient(keyFilePath)
+ if err != nil {
+ return nil, err
+ }
+ s, err := cloudmonitoring.New(c)
+ if err != nil {
+ return nil, fmt.Errorf("New() failed: %v", err)
+ }
+ return s, nil
+}
diff --git a/gcm/gcm_test.go b/gcm/gcm_test.go
new file mode 100644
index 0000000..d3fbf4e
--- /dev/null
+++ b/gcm/gcm_test.go
@@ -0,0 +1,122 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gcm
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+
+ cloudmonitoring "google.golang.org/api/monitoring/v3"
+)
+
+func TestCreateMetric(t *testing.T) {
+ type testCase struct {
+ metricType string
+ description string
+ valueType string
+ includeGCELabels bool
+ extraLabels []labelData
+ expectedMetric *cloudmonitoring.MetricDescriptor
+ }
+ testCases := []testCase{
+ testCase{
+ metricType: "test",
+ description: "this is a test",
+ valueType: "double",
+ includeGCELabels: false,
+ extraLabels: nil,
+ expectedMetric: &cloudmonitoring.MetricDescriptor{
+ Type: fmt.Sprintf("%s/vanadium/test", customMetricPrefix),
+ Description: "this is a test",
+ MetricKind: "gauge",
+ ValueType: "double",
+ Labels: []*cloudmonitoring.LabelDescriptor{
+ &cloudmonitoring.LabelDescriptor{
+ Key: "metric_name",
+ Description: "The name of the metric.",
+ ValueType: "string",
+ },
+ },
+ },
+ },
+ testCase{
+ metricType: "test2",
+ description: "this is a test2",
+ valueType: "string",
+ includeGCELabels: true,
+ extraLabels: nil,
+ expectedMetric: &cloudmonitoring.MetricDescriptor{
+ Type: fmt.Sprintf("%s/vanadium/test2", customMetricPrefix),
+ Description: "this is a test2",
+ MetricKind: "gauge",
+ ValueType: "string",
+ Labels: []*cloudmonitoring.LabelDescriptor{
+ &cloudmonitoring.LabelDescriptor{
+ Key: "gce_instance",
+ Description: "The name of the GCE instance associated with this metric.",
+ ValueType: "string",
+ },
+ &cloudmonitoring.LabelDescriptor{
+ Key: "gce_zone",
+ Description: "The zone of the GCE instance associated with this metric.",
+ ValueType: "string",
+ },
+ &cloudmonitoring.LabelDescriptor{
+ Key: "metric_name",
+ Description: "The name of the metric.",
+ ValueType: "string",
+ },
+ },
+ },
+ },
+ testCase{
+ metricType: "test3",
+ description: "this is a test3",
+ valueType: "double",
+ includeGCELabels: true,
+ extraLabels: []labelData{
+ labelData{
+ key: "extraLabel",
+ description: "this is an extra label",
+ },
+ },
+ expectedMetric: &cloudmonitoring.MetricDescriptor{
+ Type: fmt.Sprintf("%s/vanadium/test3", customMetricPrefix),
+ Description: "this is a test3",
+ MetricKind: "gauge",
+ ValueType: "double",
+ Labels: []*cloudmonitoring.LabelDescriptor{
+ &cloudmonitoring.LabelDescriptor{
+ Key: "gce_instance",
+ Description: "The name of the GCE instance associated with this metric.",
+ ValueType: "string",
+ },
+ &cloudmonitoring.LabelDescriptor{
+ Key: "gce_zone",
+ Description: "The zone of the GCE instance associated with this metric.",
+ ValueType: "string",
+ },
+ &cloudmonitoring.LabelDescriptor{
+ Key: "metric_name",
+ Description: "The name of the metric.",
+ ValueType: "string",
+ },
+ &cloudmonitoring.LabelDescriptor{
+ Key: "extraLabel",
+ Description: "this is an extra label",
+ ValueType: "string",
+ },
+ },
+ },
+ },
+ }
+ for _, test := range testCases {
+ got := createMetric(test.metricType, test.description, test.valueType, test.includeGCELabels, test.extraLabels)
+ if !reflect.DeepEqual(got, test.expectedMetric) {
+ t.Fatalf("want %#v, got %#v", test.expectedMetric, got)
+ }
+ }
+}