Commit c4aaf472 authored by Hemant Kumar's avatar Hemant Kumar

Implement API usage metrics for gce

This PR implements tracking of GCE API usage via prometheus metrics.
parent c01baaf5
...@@ -23,6 +23,7 @@ go_library( ...@@ -23,6 +23,7 @@ go_library(
"gce_instancegroup.go", "gce_instancegroup.go",
"gce_instances.go", "gce_instances.go",
"gce_loadbalancer.go", "gce_loadbalancer.go",
"gce_metrics.go",
"gce_op.go", "gce_op.go",
"gce_routes.go", "gce_routes.go",
"gce_staticip.go", "gce_staticip.go",
...@@ -42,10 +43,12 @@ go_library( ...@@ -42,10 +43,12 @@ go_library(
"//vendor:cloud.google.com/go/compute/metadata", "//vendor:cloud.google.com/go/compute/metadata",
"//vendor:github.com/golang/glog", "//vendor:github.com/golang/glog",
"//vendor:github.com/prometheus/client_golang/prometheus", "//vendor:github.com/prometheus/client_golang/prometheus",
"//vendor:golang.org/x/net/context",
"//vendor:golang.org/x/oauth2", "//vendor:golang.org/x/oauth2",
"//vendor:golang.org/x/oauth2/google", "//vendor:golang.org/x/oauth2/google",
"//vendor:google.golang.org/api/compute/v1", "//vendor:google.golang.org/api/compute/v1",
"//vendor:google.golang.org/api/container/v1", "//vendor:google.golang.org/api/container/v1",
"//vendor:google.golang.org/api/gensupport",
"//vendor:google.golang.org/api/googleapi", "//vendor:google.golang.org/api/googleapi",
"//vendor:gopkg.in/gcfg.v1", "//vendor:gopkg.in/gcfg.v1",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
......
...@@ -20,11 +20,13 @@ import ( ...@@ -20,11 +20,13 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"net/http"
"regexp" "regexp"
"strings" "strings"
"time" "time"
"cloud.google.com/go/compute/metadata" "cloud.google.com/go/compute/metadata"
"golang.org/x/net/context"
"gopkg.in/gcfg.v1" "gopkg.in/gcfg.v1"
...@@ -38,6 +40,7 @@ import ( ...@@ -38,6 +40,7 @@ import (
"golang.org/x/oauth2/google" "golang.org/x/oauth2/google"
compute "google.golang.org/api/compute/v1" compute "google.golang.org/api/compute/v1"
container "google.golang.org/api/container/v1" container "google.golang.org/api/container/v1"
"google.golang.org/api/gensupport"
) )
const ( const (
...@@ -100,12 +103,47 @@ type Config struct { ...@@ -100,12 +103,47 @@ type Config struct {
} }
} }
// ApiWithNamespace stores api and namespace in context
type apiWithNamespace struct {
namespace string
apiCall string
}
func init() { func init() {
registerMetrics()
cloudprovider.RegisterCloudProvider( cloudprovider.RegisterCloudProvider(
ProviderName, ProviderName,
func(config io.Reader) (cloudprovider.Interface, error) { func(config io.Reader) (cloudprovider.Interface, error) {
return newGCECloud(config) return newGCECloud(config)
}) })
gensupport.RegisterHook(trackAPILatency)
}
func trackAPILatency(ctx context.Context, req *http.Request) func(resp *http.Response) {
requestTime := time.Now()
t := ctx.Value("kube-api-namespace")
apiNamespace, ok := t.(apiWithNamespace)
if !ok {
return nil
}
apiResponseReceived := func(resp *http.Response) {
timeTaken := time.Since(requestTime).Seconds()
if mi, ok := gceMetricMap[apiNamespace.apiCall]; ok {
mi.WithLabelValues(apiNamespace.namespace).Observe(timeTaken)
}
}
return apiResponseReceived
}
func contextWithNamespace(namespace string, apiCall string) context.Context {
rootContext := context.Background()
apiNamespace := apiWithNamespace{
namespace: namespace,
apiCall: apiCall,
}
return context.WithValue(rootContext, "kube-api-namespace", apiNamespace)
} }
// Raw access to the underlying GCE service, probably should only be used for e2e tests // Raw access to the underlying GCE service, probably should only be used for e2e tests
......
...@@ -98,8 +98,8 @@ func (gce *GCECloud) AttachDisk(diskName string, nodeName types.NodeName, readOn ...@@ -98,8 +98,8 @@ func (gce *GCECloud) AttachDisk(diskName string, nodeName types.NodeName, readOn
readWrite = "READ_ONLY" readWrite = "READ_ONLY"
} }
attachedDisk := gce.convertDiskToAttachedDisk(disk, readWrite) attachedDisk := gce.convertDiskToAttachedDisk(disk, readWrite)
dc := contextWithNamespace(diskName, "gce_attach_disk")
attachOp, err := gce.service.Instances.AttachDisk(gce.projectID, disk.Zone, instance.Name, attachedDisk).Do() attachOp, err := gce.service.Instances.AttachDisk(gce.projectID, disk.Zone, instance.Name, attachedDisk).Context(dc).Do()
if err != nil { if err != nil {
return err return err
} }
...@@ -122,8 +122,8 @@ func (gce *GCECloud) DetachDisk(devicePath string, nodeName types.NodeName) erro ...@@ -122,8 +122,8 @@ func (gce *GCECloud) DetachDisk(devicePath string, nodeName types.NodeName) erro
return fmt.Errorf("error getting instance %q", instanceName) return fmt.Errorf("error getting instance %q", instanceName)
} }
dc := contextWithNamespace(devicePath, "gce_detach_disk")
detachOp, err := gce.service.Instances.DetachDisk(gce.projectID, inst.Zone, inst.Name, devicePath).Do() detachOp, err := gce.service.Instances.DetachDisk(gce.projectID, inst.Zone, inst.Name, devicePath).Context(dc).Do()
if err != nil { if err != nil {
return err return err
} }
...@@ -227,8 +227,8 @@ func (gce *GCECloud) CreateDisk(name string, diskType string, zone string, sizeG ...@@ -227,8 +227,8 @@ func (gce *GCECloud) CreateDisk(name string, diskType string, zone string, sizeG
Description: tagsStr, Description: tagsStr,
Type: diskTypeUri, Type: diskTypeUri,
} }
dc := contextWithNamespace(name, "gce_disk_insert")
createOp, err := gce.service.Disks.Insert(gce.projectID, zone, diskToCreate).Do() createOp, err := gce.service.Disks.Insert(gce.projectID, zone, diskToCreate).Context(dc).Do()
if err != nil { if err != nil {
return err return err
} }
...@@ -303,7 +303,8 @@ func (gce *GCECloud) GetAutoLabelsForPD(name string, zone string) (map[string]st ...@@ -303,7 +303,8 @@ func (gce *GCECloud) GetAutoLabelsForPD(name string, zone string) (map[string]st
// Returns a gceDisk for the disk, if it is found in the specified zone. // Returns a gceDisk for the disk, if it is found in the specified zone.
// If not found, returns (nil, nil) // If not found, returns (nil, nil)
func (gce *GCECloud) findDiskByName(diskName string, zone string) (*gceDisk, error) { func (gce *GCECloud) findDiskByName(diskName string, zone string) (*gceDisk, error) {
disk, err := gce.service.Disks.Get(gce.projectID, zone, diskName).Do() dc := contextWithNamespace(diskName, "gce_list_disk")
disk, err := gce.service.Disks.Get(gce.projectID, zone, diskName).Context(dc).Do()
if err == nil { if err == nil {
d := &gceDisk{ d := &gceDisk{
Zone: lastComponent(disk.Zone), Zone: lastComponent(disk.Zone),
...@@ -387,7 +388,8 @@ func (gce *GCECloud) doDeleteDisk(diskToDelete string) error { ...@@ -387,7 +388,8 @@ func (gce *GCECloud) doDeleteDisk(diskToDelete string) error {
return err return err
} }
deleteOp, err := gce.service.Disks.Delete(gce.projectID, disk.Zone, disk.Name).Do() dc := contextWithNamespace(diskToDelete, "gce_disk_delete")
deleteOp, err := gce.service.Disks.Delete(gce.projectID, disk.Zone, disk.Name).Context(dc).Do()
if err != nil { if err != nil {
return err return err
} }
......
...@@ -283,7 +283,8 @@ func (gce *GCECloud) getInstanceByName(name string) (*gceInstance, error) { ...@@ -283,7 +283,8 @@ func (gce *GCECloud) getInstanceByName(name string) (*gceInstance, error) {
// Avoid changing behaviour when not managing multiple zones // Avoid changing behaviour when not managing multiple zones
for _, zone := range gce.managedZones { for _, zone := range gce.managedZones {
name = canonicalizeInstanceName(name) name = canonicalizeInstanceName(name)
res, err := gce.service.Instances.Get(gce.projectID, zone, name).Do() dc := contextWithNamespace(name, "gce_instance_list")
res, err := gce.service.Instances.Get(gce.projectID, zone, name).Context(dc).Do()
if err != nil { if err != nil {
glog.Errorf("getInstanceByName: failed to get instance %s; err: %v", name, err) glog.Errorf("getInstanceByName: failed to get instance %s; err: %v", name, err)
......
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gce
import "github.com/prometheus/client_golang/prometheus"
var gceMetricMap = map[string]*prometheus.HistogramVec{
"gce_instance_list": prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "gce_instance_list_duration_seconds",
Help: "Latency of instance listing calls",
},
[]string{"namespace"},
),
"gce_disk_insert": prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "gce_disk_insert_duration_seconds",
Help: "Latency of disk insert calls",
},
[]string{"namespace"},
),
"gce_disk_delete": prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "gce_disk_delete_duration_seconds",
Help: "Latency of disk delete calls",
},
[]string{"namespace"},
),
"gce_attach_disk": prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "gce_attach_disk_duration_seconds",
Help: "Latency of attach disk calls",
},
[]string{"namespace"},
),
"gce_detach_disk": prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "gce_detach_disk_duration_seconds",
Help: "Latency of detach disk calls",
},
[]string{"namespace"},
),
"gce_list_disk": prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "gce_list_disk_duration_seconds",
Help: "Latency of list disk calls",
},
[]string{"namespace"},
),
}
func registerMetrics() {
for _, metric := range gceMetricMap {
prometheus.MustRegister(metric)
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment