add additional documentation around exposed functionality

This commit is contained in:
Han Kang 2019-05-01 16:28:52 -07:00
parent e6fbb593bb
commit 634ab0be53
6 changed files with 93 additions and 54 deletions

View File

@ -21,24 +21,24 @@ import (
"github.com/prometheus/client_golang/prometheus"
)
// kubeCounter is our internal representation for our wrapping struct around prometheus
// counters. kubeCounter implements both KubeCollector and KubeCounter.
type kubeCounter struct {
KubeCounter
// Counter is our internal representation for our wrapping struct around prometheus
// counters. Counter implements both KubeCollector and CounterMetric.
type Counter struct {
CounterMetric
*CounterOpts
lazyMetric
selfCollector
}
// NewCounter returns an object which satisfies the KubeCollector and KubeCounter interfaces.
// NewCounter returns an object which satisfies the KubeCollector and CounterMetric interfaces.
// However, the object returned will not measure anything unless the collector is first
// registered, since the metric is lazily instantiated.
func NewCounter(opts *CounterOpts) *kubeCounter {
func NewCounter(opts *CounterOpts) *Counter {
// todo: handle defaulting better
if opts.StabilityLevel == "" {
opts.StabilityLevel = ALPHA
}
kc := &kubeCounter{
kc := &Counter{
CounterOpts: opts,
lazyMetric: lazyMetric{},
}
@ -47,20 +47,20 @@ func NewCounter(opts *CounterOpts) *kubeCounter {
return kc
}
// setPrometheusCounter sets the underlying KubeCounter object, i.e. the thing that does the measurement.
func (c *kubeCounter) setPrometheusCounter(counter prometheus.Counter) {
c.KubeCounter = counter
// setPrometheusCounter sets the underlying CounterMetric object, i.e. the thing that does the measurement.
func (c *Counter) setPrometheusCounter(counter prometheus.Counter) {
c.CounterMetric = counter
c.initSelfCollection(counter)
}
// DeprecatedVersion returns a pointer to the Version or nil
func (c *kubeCounter) DeprecatedVersion() *semver.Version {
func (c *Counter) DeprecatedVersion() *semver.Version {
return c.CounterOpts.DeprecatedVersion
}
// initializeMetric invocation creates the actual underlying Counter. Until this method is called
// the underlying counter is a no-op.
func (c *kubeCounter) initializeMetric() {
func (c *Counter) initializeMetric() {
c.CounterOpts.annotateStabilityLevel()
// this actually creates the underlying prometheus counter.
c.setPrometheusCounter(prometheus.NewCounter(c.CounterOpts.toPromCounterOpts()))
@ -68,25 +68,25 @@ func (c *kubeCounter) initializeMetric() {
// initializeDeprecatedMetric invocation creates the actual (but deprecated) Counter. Until this method
// is called the underlying counter is a no-op.
func (c *kubeCounter) initializeDeprecatedMetric() {
func (c *Counter) initializeDeprecatedMetric() {
c.CounterOpts.markDeprecated()
c.initializeMetric()
}
// kubeCounterVec is the internal representation of our wrapping struct around prometheus
// counterVecs. kubeCounterVec implements both KubeCollector and KubeCounterVec.
type kubeCounterVec struct {
// CounterVec is the internal representation of our wrapping struct around prometheus
// counterVecs. CounterVec implements both KubeCollector and CounterVecMetric.
type CounterVec struct {
*prometheus.CounterVec
*CounterOpts
lazyMetric
originalLabels []string
}
// NewCounterVec returns an object which satisfies the KubeCollector and KubeCounterVec interfaces.
// NewCounterVec returns an object which satisfies the KubeCollector and CounterVecMetric interfaces.
// However, the object returned will not measure anything unless the collector is first
// registered, since the metric is lazily instantiated.
func NewCounterVec(opts *CounterOpts, labels []string) *kubeCounterVec {
cv := &kubeCounterVec{
func NewCounterVec(opts *CounterOpts, labels []string) *CounterVec {
cv := &CounterVec{
CounterVec: noopCounterVec,
CounterOpts: opts,
originalLabels: labels,
@ -97,19 +97,19 @@ func NewCounterVec(opts *CounterOpts, labels []string) *kubeCounterVec {
}
// DeprecatedVersion returns a pointer to the Version or nil
func (v *kubeCounterVec) DeprecatedVersion() *semver.Version {
func (v *CounterVec) DeprecatedVersion() *semver.Version {
return v.CounterOpts.DeprecatedVersion
}
// initializeMetric invocation creates the actual underlying CounterVec. Until this method is called
// the underlying counterVec is a no-op.
func (v *kubeCounterVec) initializeMetric() {
func (v *CounterVec) initializeMetric() {
v.CounterVec = prometheus.NewCounterVec(v.CounterOpts.toPromCounterOpts(), v.originalLabels)
}
// initializeDeprecatedMetric invocation creates the actual (but deprecated) CounterVec. Until this method is called
// the underlying counterVec is a no-op.
func (v *kubeCounterVec) initializeDeprecatedMetric() {
func (v *CounterVec) initializeDeprecatedMetric() {
v.CounterOpts.markDeprecated()
v.initializeMetric()
}
@ -121,17 +121,23 @@ func (v *kubeCounterVec) initializeDeprecatedMetric() {
// for perpetuity (i.e. throughout application lifecycle).
//
// For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/counter.go#L179-L197
//
// This method returns a no-op metric if the metric is not actually created/registered, avoiding that
// memory leak.
func (v *kubeCounterVec) WithLabelValues(lvs ...string) KubeCounter {
// WithLabelValues returns the Counter for the given slice of label
// values (same order as the VariableLabels in Desc). If that combination of
// label values is accessed for the first time, a new Counter is created IFF the counterVec
// has been registered to a metrics registry.
func (v *CounterVec) WithLabelValues(lvs ...string) CounterMetric {
if !v.IsCreated() {
return noop // return no-op counter
}
return v.CounterVec.WithLabelValues(lvs...)
}
func (v *kubeCounterVec) With(labels prometheus.Labels) KubeCounter {
// With returns the Counter for the given Labels map (the label names
// must match those of the VariableLabels in Desc). If that label map is
// accessed for the first time, a new Counter is created IFF the counterVec has
// been registered to a metrics registry.
func (v *CounterVec) With(labels prometheus.Labels) CounterMetric {
if !v.IsCreated() {
return noop // return no-op counter
}

View File

@ -25,7 +25,7 @@ import (
)
/*
This extends the prometheus.Collector interface to allow customization of the metric
KubeCollector extends the prometheus.Collector interface to allow customization of the metric
registration process. Defer metric initialization until Create() is called, which then
delegates to the underlying metric's initializeMetric or initializeDeprecatedMetric
method call depending on whether the metric is deprecated or not.

View File

@ -41,13 +41,19 @@ type KubeOpts struct {
StabilityLevel StabilityLevel
}
// StabilityLevel represents the API guarantees for a given defined metric.
type StabilityLevel string
const (
ALPHA StabilityLevel = "ALPHA"
// ALPHA metrics have no stability guarantees, as such, labels may
// be arbitrarily added/removed and the metric may be deleted at any time.
ALPHA StabilityLevel = "ALPHA"
// STABLE metrics are guaranteed not be mutated and removal is governed by
// the deprecation policy outlined in by the control plane metrics stability KEP.
STABLE StabilityLevel = "STABLE"
)
// CounterOpts is an alias for Opts. See there for doc comments.
type CounterOpts KubeOpts
// Modify help description on the metric description.

View File

@ -24,8 +24,13 @@ import (
"k8s.io/kubernetes/pkg/version"
)
// DefaultGlobalRegistry is a stub for the global registry which prometheus client
// currently uses.
var DefaultGlobalRegistry = NewKubeRegistry()
// KubeRegistry is a wrapper around a prometheus registry-type object. Upon initialization
// the kubernetes binary version information is loaded into the registry object, so that
// automatic behavior can be configured for metric versioning.
type KubeRegistry struct {
PromRegistry
version semver.Version
@ -43,6 +48,11 @@ func MustRegister(cs ...KubeCollector) {
DefaultGlobalRegistry.MustRegister(cs...)
}
// Register registers a new Collector to be included in metrics
// collection. It returns an error if the descriptors provided by the
// Collector are invalid or if they — in combination with descriptors of
// already registered Collectors — do not fulfill the consistency and
// uniqueness criteria described in the documentation of metric.Desc.
func (kr *KubeRegistry) Register(c KubeCollector) error {
if c.Create(&kr.version) {
return kr.PromRegistry.Register(c)
@ -50,6 +60,9 @@ func (kr *KubeRegistry) Register(c KubeCollector) error {
return nil
}
// MustRegister works like Register but registers any number of
// Collectors and panics upon the first registration that causes an
// error.
func (kr *KubeRegistry) MustRegister(cs ...KubeCollector) {
metrics := make([]prometheus.Collector, 0, len(cs))
for _, c := range cs {
@ -60,14 +73,29 @@ func (kr *KubeRegistry) MustRegister(cs ...KubeCollector) {
kr.PromRegistry.MustRegister(metrics...)
}
// Unregister unregisters the Collector that equals the Collector passed
// in as an argument. (Two Collectors are considered equal if their
// Describe method yields the same set of descriptors.) The function
// returns whether a Collector was unregistered. Note that an unchecked
// Collector cannot be unregistered (as its Describe method does not
// yield any descriptor).
func (kr *KubeRegistry) Unregister(collector KubeCollector) bool {
return kr.PromRegistry.Unregister(collector)
}
// Gather calls the Collect method of the registered Collectors and then
// gathers the collected metrics into a lexicographically sorted slice
// of uniquely named MetricFamily protobufs. Gather ensures that the
// returned slice is valid and self-consistent so that it can be used
// for valid exposition. As an exception to the strict consistency
// requirements described for metric.Desc, Gather will tolerate
// different sets of label names for metrics of the same metric family.
func (kr *KubeRegistry) Gather() ([]*dto.MetricFamily, error) {
return kr.PromRegistry.Gather()
}
// NewKubeRegistry creates a new kubernetes metric registry, loading in the kubernetes
// version information available to the binary.
func NewKubeRegistry() *KubeRegistry {
v, err := parseVersion(version.Get())
if err != nil {

View File

@ -69,7 +69,7 @@ var (
func TestRegister(t *testing.T) {
var tests = []struct {
desc string
metrics []*kubeCounter
metrics []*Counter
registryVersion *semver.Version
expectedErrors []error
expectedIsCreatedValues []bool
@ -78,7 +78,7 @@ func TestRegister(t *testing.T) {
}{
{
desc: "test alpha metric",
metrics: []*kubeCounter{alphaCounter},
metrics: []*Counter{alphaCounter},
registryVersion: &v115,
expectedErrors: []error{nil},
expectedIsCreatedValues: []bool{true},
@ -87,7 +87,7 @@ func TestRegister(t *testing.T) {
},
{
desc: "test registering same metric multiple times",
metrics: []*kubeCounter{alphaCounter, alphaCounter},
metrics: []*Counter{alphaCounter, alphaCounter},
registryVersion: &v115,
expectedErrors: []error{nil, prometheus.AlreadyRegisteredError{}},
expectedIsCreatedValues: []bool{true, true},
@ -96,7 +96,7 @@ func TestRegister(t *testing.T) {
},
{
desc: "test alpha deprecated metric",
metrics: []*kubeCounter{alphaDeprecatedCounter},
metrics: []*Counter{alphaDeprecatedCounter},
registryVersion: &v115,
expectedErrors: []error{nil, prometheus.AlreadyRegisteredError{}},
expectedIsCreatedValues: []bool{true},
@ -105,7 +105,7 @@ func TestRegister(t *testing.T) {
},
{
desc: "test alpha hidden metric",
metrics: []*kubeCounter{alphaHiddenCounter},
metrics: []*Counter{alphaHiddenCounter},
registryVersion: &v115,
expectedErrors: []error{nil, prometheus.AlreadyRegisteredError{}},
expectedIsCreatedValues: []bool{false},
@ -139,43 +139,43 @@ func TestRegister(t *testing.T) {
func TestMustRegister(t *testing.T) {
var tests = []struct {
desc string
metrics []*kubeCounter
metrics []*Counter
registryVersion *semver.Version
expectedPanics []bool
}{
{
desc: "test alpha metric",
metrics: []*kubeCounter{alphaCounter},
metrics: []*Counter{alphaCounter},
registryVersion: &v115,
expectedPanics: []bool{false},
},
{
desc: "test registering same metric multiple times",
metrics: []*kubeCounter{alphaCounter, alphaCounter},
metrics: []*Counter{alphaCounter, alphaCounter},
registryVersion: &v115,
expectedPanics: []bool{false, true},
},
{
desc: "test alpha deprecated metric",
metrics: []*kubeCounter{alphaDeprecatedCounter},
metrics: []*Counter{alphaDeprecatedCounter},
registryVersion: &v115,
expectedPanics: []bool{false},
},
{
desc: "test must registering same deprecated metric",
metrics: []*kubeCounter{alphaDeprecatedCounter, alphaDeprecatedCounter},
metrics: []*Counter{alphaDeprecatedCounter, alphaDeprecatedCounter},
registryVersion: &v115,
expectedPanics: []bool{false, true},
},
{
desc: "test alpha hidden metric",
metrics: []*kubeCounter{alphaHiddenCounter},
metrics: []*Counter{alphaHiddenCounter},
registryVersion: &v115,
expectedPanics: []bool{false},
},
{
desc: "test must registering same hidden metric",
metrics: []*kubeCounter{alphaHiddenCounter, alphaHiddenCounter},
metrics: []*Counter{alphaHiddenCounter, alphaHiddenCounter},
registryVersion: &v115,
expectedPanics: []bool{false, false}, // hidden metrics no-opt
},

View File

@ -27,38 +27,37 @@ import (
// so that we can prevent breakage if methods are ever added to prometheus
// variants of them.
/**
* Collector defines a subset of prometheus.Collector interface methods
*/
// Collector defines a subset of prometheus.Collector interface methods
type Collector interface {
Describe(chan<- *prometheus.Desc)
Collect(chan<- prometheus.Metric)
}
/**
* Metric defines a subset of prometheus.Metric interface methods
*/
// Metric defines a subset of prometheus.Metric interface methods
type Metric interface {
Desc() *prometheus.Desc
Write(*dto.Metric) error
}
// Counter is a Metric that represents a single numerical value that only ever
// CounterMetric is a Metric that represents a single numerical value that only ever
// goes up. That implies that it cannot be used to count items whose number can
// also go down, e.g. the number of currently running goroutines. Those
// "counters" are represented by Gauges.
//
// This interface defines a subset of the interface provided by prometheus.Counter
type KubeCounter interface {
// CounterMetric is an interface which defines a subset of the interface provided by prometheus.Counter
type CounterMetric interface {
Inc()
Add(float64)
}
type KubeCounterVec interface {
WithLabelValues(...string) KubeCounter
With(prometheus.Labels) KubeCounter
// CounterVecMetric is an interface which prometheus.CounterVec satisfies.
type CounterVecMetric interface {
WithLabelValues(...string) CounterMetric
With(prometheus.Labels) CounterMetric
}
// PromRegistry is an interface which implements a subset of prometheus.Registerer and
// prometheus.Gatherer interfaces
type PromRegistry interface {
Register(prometheus.Collector) error
MustRegister(...prometheus.Collector)