add constant labels to gauges and cumulative metrics (#1122)

* Remove unused GetEntry.

* adds support for constant labels on Gauge and CumulativeMetric

* fixing format on tests.

* remove unused getentry
diff --git a/metric/common.go b/metric/common.go
index f5716c9..bd6e771 100644
--- a/metric/common.go
+++ b/metric/common.go
@@ -19,6 +19,7 @@
 	"time"
 
 	"go.opencensus.io/internal/tagencoding"
+
 	"go.opencensus.io/metric/metricdata"
 )
 
@@ -30,11 +31,12 @@
 // baseMetric should not be used directly, use metric specific type such as
 // Float64Gauge or Int64Gauge.
 type baseMetric struct {
-	vals   sync.Map
-	desc   metricdata.Descriptor
-	start  time.Time
-	keys   []metricdata.LabelKey
-	bmType baseMetricType
+	vals             sync.Map
+	desc             metricdata.Descriptor
+	start            time.Time
+	keys             []metricdata.LabelKey
+	constLabelValues []metricdata.LabelValue
+	bmType           baseMetricType
 }
 
 type baseMetricType int
@@ -118,6 +120,7 @@
 }
 
 func (bm *baseMetric) entryForValues(labelVals []metricdata.LabelValue, newEntry func() baseEntry) (interface{}, error) {
+	labelVals = append(bm.constLabelValues, labelVals...)
 	if len(labelVals) != len(bm.keys) {
 		return nil, errKeyValueMismatch
 	}
diff --git a/metric/cumulative_test.go b/metric/cumulative_test.go
index 6b2110d..98bda94 100644
--- a/metric/cumulative_test.go
+++ b/metric/cumulative_test.go
@@ -19,6 +19,7 @@
 	"time"
 
 	"github.com/google/go-cmp/cmp"
+
 	"go.opencensus.io/metric/metricdata"
 )
 
@@ -83,6 +84,61 @@
 	}
 }
 
+func TestCumulativeConstLabel(t *testing.T) {
+	r := NewRegistry()
+
+	f, _ := r.AddFloat64Cumulative("TestCumulativeWithConstLabel",
+		WithLabelKeys("k1"),
+		WithConstLabel(map[metricdata.LabelKey]metricdata.LabelValue{
+			{Key: "const"}:  metricdata.NewLabelValue("same"),
+			{Key: "const2"}: metricdata.NewLabelValue("same2"),
+		}))
+
+	e, _ := f.GetEntry(metricdata.LabelValue{})
+	e.Inc(5)
+	e, _ = f.GetEntry(metricdata.NewLabelValue("k1v1"))
+	e.Inc(1)
+	m := r.Read()
+	want := []*metricdata.Metric{
+		{
+			Descriptor: metricdata.Descriptor{
+				Name: "TestCumulativeWithConstLabel",
+				LabelKeys: []metricdata.LabelKey{
+					{Key: "const"},
+					{Key: "const2"},
+					{Key: "k1"}},
+				Type: metricdata.TypeCumulativeFloat64,
+			},
+			TimeSeries: []*metricdata.TimeSeries{
+				{
+					LabelValues: []metricdata.LabelValue{
+						metricdata.NewLabelValue("same"),
+						metricdata.NewLabelValue("same2"),
+						{}},
+					Points: []metricdata.Point{
+						metricdata.NewFloat64Point(time.Time{}, 5),
+					},
+				},
+				{
+					LabelValues: []metricdata.LabelValue{
+						metricdata.NewLabelValue("same"),
+						metricdata.NewLabelValue("same2"),
+						metricdata.NewLabelValue("k1v1"),
+					},
+					Points: []metricdata.Point{
+						metricdata.NewFloat64Point(time.Time{}, 1),
+					},
+				},
+			},
+		},
+	}
+	canonicalize(m)
+	canonicalize(want)
+	if diff := cmp.Diff(m, want, cmp.Comparer(ignoreTimes)); diff != "" {
+		t.Errorf("-got +want: %s", diff)
+	}
+}
+
 func TestCumulativeMetricDescriptor(t *testing.T) {
 	r := NewRegistry()
 
diff --git a/metric/gauge_test.go b/metric/gauge_test.go
index a352d2d..9c4f269 100644
--- a/metric/gauge_test.go
+++ b/metric/gauge_test.go
@@ -16,12 +16,13 @@
 
 import (
 	"fmt"
-	"go.opencensus.io/metric/metricdata"
 	"sort"
 	"testing"
 	"time"
 
 	"github.com/google/go-cmp/cmp"
+
+	"go.opencensus.io/metric/metricdata"
 )
 
 func TestGauge(t *testing.T) {
@@ -85,6 +86,62 @@
 	}
 }
 
+func TestGaugeConstLabel(t *testing.T) {
+	r := NewRegistry()
+
+	f, _ := r.AddFloat64Gauge("TestGaugeWithConstLabel",
+		WithLabelKeys("k1"),
+		WithConstLabel(map[metricdata.LabelKey]metricdata.LabelValue{
+			{Key: "const"}:  metricdata.NewLabelValue("same"),
+			{Key: "const2"}: metricdata.NewLabelValue("same2"),
+		}))
+
+	e, _ := f.GetEntry(metricdata.LabelValue{})
+	e.Set(5)
+	e, _ = f.GetEntry(metricdata.NewLabelValue("k1v1"))
+	e.Add(1)
+	m := r.Read()
+	want := []*metricdata.Metric{
+		{
+			Descriptor: metricdata.Descriptor{
+				Name: "TestGaugeWithConstLabel",
+				LabelKeys: []metricdata.LabelKey{
+					{Key: "const"},
+					{Key: "const2"},
+					{Key: "k1"}},
+				Type: metricdata.TypeGaugeFloat64,
+			},
+			TimeSeries: []*metricdata.TimeSeries{
+				{
+					LabelValues: []metricdata.LabelValue{
+						metricdata.NewLabelValue("same"),
+						metricdata.NewLabelValue("same2"),
+						{},
+					},
+					Points: []metricdata.Point{
+						metricdata.NewFloat64Point(time.Time{}, 5),
+					},
+				},
+				{
+					LabelValues: []metricdata.LabelValue{
+						metricdata.NewLabelValue("same"),
+						metricdata.NewLabelValue("same2"),
+						metricdata.NewLabelValue("k1v1"),
+					},
+					Points: []metricdata.Point{
+						metricdata.NewFloat64Point(time.Time{}, 1),
+					},
+				},
+			},
+		},
+	}
+	canonicalize(m)
+	canonicalize(want)
+	if diff := cmp.Diff(m, want, cmp.Comparer(ignoreTimes)); diff != "" {
+		t.Errorf("-got +want: %s", diff)
+	}
+}
+
 func TestGaugeMetricDescriptor(t *testing.T) {
 	r := NewRegistry()
 
@@ -330,20 +387,18 @@
 	for _, m := range ms {
 		sort.Slice(m.TimeSeries, func(i, j int) bool {
 			// sort time series by their label values
-			iLabels := m.TimeSeries[i].LabelValues
-			jLabels := m.TimeSeries[j].LabelValues
-			for k := 0; k < len(iLabels); k++ {
-				if !iLabels[k].Present {
-					if jLabels[k].Present {
-						return true
-					}
-				} else if !jLabels[k].Present {
-					return false
-				} else {
-					return iLabels[k].Value < jLabels[k].Value
-				}
+			iStr := ""
+
+			for _, label := range m.TimeSeries[i].LabelValues {
+				iStr += fmt.Sprintf("%+v", label)
 			}
-			panic("should have returned")
+
+			jStr := ""
+			for _, label := range m.TimeSeries[j].LabelValues {
+				jStr += fmt.Sprintf("%+v", label)
+			}
+
+			return iStr < jStr
 		})
 	}
 }
diff --git a/metric/registry.go b/metric/registry.go
index ceea7e9..6c58ff9 100644
--- a/metric/registry.go
+++ b/metric/registry.go
@@ -15,6 +15,7 @@
 package metric
 
 import (
+	"sort"
 	"sync"
 	"time"
 
@@ -28,11 +29,11 @@
 	baseMetrics sync.Map
 }
 
-//TODO: [rghetia] add constant labels.
 type metricOptions struct {
-	unit      metricdata.Unit
-	labelkeys []metricdata.LabelKey
-	desc      string
+	unit        metricdata.Unit
+	labelkeys   []metricdata.LabelKey
+	constLabels map[metricdata.LabelKey]metricdata.LabelValue
+	desc        string
 }
 
 // Options apply changes to metricOptions.
@@ -70,6 +71,13 @@
 	}
 }
 
+// WithConstLabel applies provided constant label.
+func WithConstLabel(constLabels map[metricdata.LabelKey]metricdata.LabelValue) Options {
+	return func(mo *metricOptions) {
+		mo.constLabels = constLabels
+	}
+}
+
 // NewRegistry initializes a new Registry.
 func NewRegistry() *Registry {
 	return &Registry{}
@@ -236,12 +244,28 @@
 	}
 	bm.start = time.Now()
 	o := createMetricOption(mos...)
-	bm.keys = o.labelkeys
+
+	var constLabelKeys []metricdata.LabelKey
+	for k := range o.constLabels {
+		constLabelKeys = append(constLabelKeys, k)
+	}
+	sort.Slice(constLabelKeys, func(i, j int) bool {
+		return constLabelKeys[i].Key < constLabelKeys[j].Key
+	})
+
+	var constLabelValues []metricdata.LabelValue
+	for _, k := range constLabelKeys {
+		constLabelValues = append(constLabelValues, o.constLabels[k])
+	}
+
+	bm.keys = append(constLabelKeys, o.labelkeys...)
+	bm.constLabelValues = constLabelValues
+
 	bm.desc = metricdata.Descriptor{
 		Name:        name,
 		Description: o.desc,
 		Unit:        o.unit,
-		LabelKeys:   o.labelkeys,
+		LabelKeys:   bm.keys,
 		Type:        bmTypeToMetricType(bm),
 	}
 	r.baseMetrics.Store(name, bm)