Skip to content

Commit 5d3125e

Browse files
authored
Merge pull request kubernetes#85444 from RainbowMango/pr_fix_metrics_cannot_enable_issue
Provided a mechanism to re-register hidden metrics
2 parents 7907c63 + 10865d9 commit 5d3125e

File tree

3 files changed

+139
-7
lines changed

3 files changed

+139
-7
lines changed

staging/src/k8s.io/component-base/metrics/metric.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,19 @@ func (r *lazyMetric) Create(version *semver.Version) bool {
143143
return r.IsCreated()
144144
}
145145

146+
// ClearState will clear all the states marked by Create.
147+
// It intends to be used for re-register a hidden metric.
148+
func (r *lazyMetric) ClearState() {
149+
r.createLock.Lock()
150+
defer r.createLock.Unlock()
151+
152+
r.isDeprecated = false
153+
r.isHidden = false
154+
r.isCreated = false
155+
r.markDeprecationOnce = *(new(sync.Once))
156+
r.createOnce = *(new(sync.Once))
157+
}
158+
146159
/*
147160
This code is directly lifted from the prometheus codebase. It's a convenience struct which
148161
allows you satisfy the Collector interface automatically if you already satisfy the Metric interface.

staging/src/k8s.io/component-base/metrics/registry.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import (
3232
var (
3333
showHiddenOnce sync.Once
3434
showHidden atomic.Value
35+
registries []*kubeRegistry // stores all registries created by NewKubeRegistry()
36+
registriesLock sync.RWMutex
3537
)
3638

3739
// shouldHide be used to check if a specific metric with deprecated version should be hidden
@@ -77,6 +79,11 @@ func ValidateShowHiddenMetricsVersion(v string) []error {
7779
func SetShowHidden() {
7880
showHiddenOnce.Do(func() {
7981
showHidden.Store(true)
82+
83+
// re-register collectors that has been hidden in phase of last registry.
84+
for _, r := range registries {
85+
r.enableHiddenCollectors()
86+
}
8087
})
8188
}
8289

@@ -91,7 +98,12 @@ func ShouldShowHidden() bool {
9198
// will register with KubeRegistry.
9299
type Registerable interface {
93100
prometheus.Collector
101+
102+
// Create will mark deprecated state for the collector
94103
Create(version *semver.Version) bool
104+
105+
// ClearState will clear all the states marked by Create.
106+
ClearState()
95107
}
96108

97109
// KubeRegistry is an interface which implements a subset of prometheus.Registerer and
@@ -114,7 +126,9 @@ type KubeRegistry interface {
114126
// automatic behavior can be configured for metric versioning.
115127
type kubeRegistry struct {
116128
PromRegistry
117-
version semver.Version
129+
version semver.Version
130+
hiddenCollectors []Registerable // stores all collectors that has been hidden
131+
hiddenCollectorsLock sync.RWMutex
118132
}
119133

120134
// Register registers a new Collector to be included in metrics
@@ -126,6 +140,9 @@ func (kr *kubeRegistry) Register(c Registerable) error {
126140
if c.Create(&kr.version) {
127141
return kr.PromRegistry.Register(c)
128142
}
143+
144+
kr.trackHiddenCollector(c)
145+
129146
return nil
130147
}
131148

@@ -137,6 +154,8 @@ func (kr *kubeRegistry) MustRegister(cs ...Registerable) {
137154
for _, c := range cs {
138155
if c.Create(&kr.version) {
139156
metrics = append(metrics, c)
157+
} else {
158+
kr.trackHiddenCollector(c)
140159
}
141160
}
142161
kr.PromRegistry.MustRegister(metrics...)
@@ -204,17 +223,43 @@ func (kr *kubeRegistry) Gather() ([]*dto.MetricFamily, error) {
204223
return kr.PromRegistry.Gather()
205224
}
206225

226+
// trackHiddenCollector stores all hidden collectors.
227+
func (kr *kubeRegistry) trackHiddenCollector(c Registerable) {
228+
kr.hiddenCollectorsLock.Lock()
229+
defer kr.hiddenCollectorsLock.Unlock()
230+
231+
kr.hiddenCollectors = append(kr.hiddenCollectors, c)
232+
}
233+
234+
// enableHiddenCollectors will re-register all of the hidden collectors.
235+
func (kr *kubeRegistry) enableHiddenCollectors() {
236+
kr.hiddenCollectorsLock.Lock()
237+
defer kr.hiddenCollectorsLock.Unlock()
238+
239+
for _, c := range kr.hiddenCollectors {
240+
c.ClearState()
241+
kr.MustRegister(c)
242+
}
243+
kr.hiddenCollectors = nil
244+
}
245+
207246
func newKubeRegistry(v apimachineryversion.Info) *kubeRegistry {
208247
r := &kubeRegistry{
209248
PromRegistry: prometheus.NewRegistry(),
210249
version: parseVersion(v),
211250
}
251+
252+
registriesLock.Lock()
253+
defer registriesLock.Unlock()
254+
registries = append(registries, r)
255+
212256
return r
213257
}
214258

215259
// NewKubeRegistry creates a new vanilla Registry without any Collectors
216260
// pre-registered.
217261
func NewKubeRegistry() KubeRegistry {
218262
r := newKubeRegistry(version.Get())
263+
219264
return r
220265
}

staging/src/k8s.io/component-base/metrics/registry_test.go

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@ limitations under the License.
1717
package metrics
1818

1919
import (
20+
"strings"
21+
"sync"
2022
"testing"
2123

2224
"github.com/blang/semver"
2325
"github.com/prometheus/client_golang/prometheus"
26+
"github.com/prometheus/client_golang/prometheus/testutil"
2427
"github.com/stretchr/testify/assert"
2528

2629
apimachineryversion "k8s.io/apimachinery/pkg/version"
@@ -204,12 +207,6 @@ func TestMustRegister(t *testing.T) {
204207
registryVersion: &v115,
205208
expectedPanics: []bool{false},
206209
},
207-
{
208-
desc: "test must registering same hidden metric",
209-
metrics: []*Counter{alphaHiddenCounter, alphaHiddenCounter},
210-
registryVersion: &v115,
211-
expectedPanics: []bool{false, false}, // hidden metrics no-opt
212-
},
213210
}
214211

215212
for _, test := range tests {
@@ -317,3 +314,80 @@ func TestValidateShowHiddenMetricsVersion(t *testing.T) {
317314
})
318315
}
319316
}
317+
318+
func TestEnableHiddenMetrics(t *testing.T) {
319+
currentVersion := apimachineryversion.Info{
320+
Major: "1",
321+
Minor: "17",
322+
GitVersion: "v1.17.1-alpha-1.12345",
323+
}
324+
325+
var tests = []struct {
326+
name string
327+
fqName string
328+
counter *Counter
329+
mustRegister bool
330+
expectedMetric string
331+
}{
332+
{
333+
name: "hide by register",
334+
fqName: "hidden_metric_register",
335+
counter: NewCounter(&CounterOpts{
336+
Name: "hidden_metric_register",
337+
Help: "counter help",
338+
StabilityLevel: STABLE,
339+
DeprecatedVersion: "1.16.0",
340+
}),
341+
mustRegister: false,
342+
expectedMetric: `
343+
# HELP hidden_metric_register [STABLE] (Deprecated since 1.16.0) counter help
344+
# TYPE hidden_metric_register counter
345+
hidden_metric_register 1
346+
`,
347+
},
348+
{
349+
name: "hide by must register",
350+
fqName: "hidden_metric_must_register",
351+
counter: NewCounter(&CounterOpts{
352+
Name: "hidden_metric_must_register",
353+
Help: "counter help",
354+
StabilityLevel: STABLE,
355+
DeprecatedVersion: "1.16.0",
356+
}),
357+
mustRegister: true,
358+
expectedMetric: `
359+
# HELP hidden_metric_must_register [STABLE] (Deprecated since 1.16.0) counter help
360+
# TYPE hidden_metric_must_register counter
361+
hidden_metric_must_register 1
362+
`,
363+
},
364+
}
365+
366+
for _, test := range tests {
367+
tc := test
368+
t.Run(tc.name, func(t *testing.T) {
369+
registry := newKubeRegistry(currentVersion)
370+
if tc.mustRegister {
371+
registry.MustRegister(tc.counter)
372+
} else {
373+
_ = registry.Register(tc.counter)
374+
}
375+
376+
tc.counter.Inc() // no-ops, because counter hasn't been initialized
377+
if err := testutil.GatherAndCompare(registry, strings.NewReader(""), tc.fqName); err != nil {
378+
t.Fatal(err)
379+
}
380+
381+
SetShowHidden()
382+
defer func() {
383+
showHiddenOnce = *new(sync.Once)
384+
showHidden.Store(false)
385+
}()
386+
387+
tc.counter.Inc()
388+
if err := testutil.GatherAndCompare(registry, strings.NewReader(tc.expectedMetric), tc.fqName); err != nil {
389+
t.Fatal(err)
390+
}
391+
})
392+
}
393+
}

0 commit comments

Comments
 (0)