Skip to content

Commit aba0b31

Browse files
committed
Changed Kubelet client and serving cert TTL/Expiry certs to use gaugefunc for calculating time remaining.
1 parent 896b77e commit aba0b31

File tree

9 files changed

+85
-107
lines changed

9 files changed

+85
-107
lines changed

pkg/kubelet/certificate/kubelet.go

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ import (
2121
"crypto/x509"
2222
"crypto/x509/pkix"
2323
"fmt"
24+
"math"
2425
"net"
2526
"sort"
27+
"time"
2628

2729
certificates "k8s.io/api/certificates/v1beta1"
2830
v1 "k8s.io/api/core/v1"
@@ -52,15 +54,6 @@ func NewKubeletServerCertificateManager(kubeClient clientset.Interface, kubeCfg
5254
if err != nil {
5355
return nil, fmt.Errorf("failed to initialize server certificate store: %v", err)
5456
}
55-
certificateExpiration := compbasemetrics.NewGauge(
56-
&compbasemetrics.GaugeOpts{
57-
Subsystem: metrics.KubeletSubsystem,
58-
Name: "certificate_manager_server_expiration_seconds",
59-
Help: "Gauge of the lifetime of a certificate. The value is the date the certificate will expire in seconds since January 1, 1970 UTC.",
60-
StabilityLevel: compbasemetrics.ALPHA,
61-
},
62-
)
63-
legacyregistry.MustRegister(certificateExpiration)
6457
var certificateRenewFailure = compbasemetrics.NewCounter(
6558
&compbasemetrics.CounterOpts{
6659
Subsystem: metrics.KubeletSubsystem,
@@ -129,13 +122,30 @@ func NewKubeletServerCertificateManager(kubeClient clientset.Interface, kubeCfg
129122
certificates.UsageServerAuth,
130123
},
131124
CertificateStore: certificateStore,
132-
CertificateExpiration: certificateExpiration,
133125
CertificateRotation: certificateRotationAge,
134126
CertificateRenewFailure: certificateRenewFailure,
135127
})
136128
if err != nil {
137129
return nil, fmt.Errorf("failed to initialize server certificate manager: %v", err)
138130
}
131+
legacyregistry.RawMustRegister(compbasemetrics.NewGaugeFunc(
132+
compbasemetrics.GaugeOpts{
133+
Subsystem: metrics.KubeletSubsystem,
134+
Name: "certificate_manager_server_ttl_seconds",
135+
Help: "Gauge of the shortest TTL (time-to-live) of " +
136+
"the Kubelet's serving certificate. The value is in seconds " +
137+
"until certificate expiry (negative if already expired). If " +
138+
"serving certificate is invalid or unused, the value will " +
139+
"be +INF.",
140+
StabilityLevel: compbasemetrics.ALPHA,
141+
},
142+
func() float64 {
143+
if c := m.Current(); c != nil && c.Leaf != nil {
144+
return c.Leaf.NotAfter.Sub(time.Now()).Seconds()
145+
}
146+
return math.Inf(1)
147+
},
148+
))
139149
return m, nil
140150
}
141151

@@ -252,7 +262,6 @@ func NewKubeletClientCertificateManager(
252262
BootstrapKeyPEM: bootstrapKeyData,
253263

254264
CertificateStore: certificateStore,
255-
CertificateExpiration: certificateExpiration,
256265
CertificateRenewFailure: certificateRenewFailure,
257266
})
258267
if err != nil {

staging/src/k8s.io/client-go/plugin/pkg/client/auth/exec/BUILD

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ go_test(
4141
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
4242
"//staging/src/k8s.io/client-go/pkg/apis/clientauthentication:go_default_library",
4343
"//staging/src/k8s.io/client-go/tools/clientcmd/api:go_default_library",
44-
"//staging/src/k8s.io/client-go/tools/metrics:go_default_library",
4544
"//staging/src/k8s.io/client-go/transport:go_default_library",
4645
],
4746
)

staging/src/k8s.io/client-go/plugin/pkg/client/auth/exec/exec.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,6 @@ func (a *Authenticator) cert() (*tls.Certificate, error) {
262262
func (a *Authenticator) getCreds() (*credentials, error) {
263263
a.mu.Lock()
264264
defer a.mu.Unlock()
265-
defer expirationMetrics.report(time.Now)
266265

267266
if a.cachedCreds != nil && !a.credsExpired() {
268267
return a.cachedCreds, nil

staging/src/k8s.io/client-go/plugin/pkg/client/auth/exec/metrics.go

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,25 @@ import (
2424
)
2525

2626
type certificateExpirationTracker struct {
27-
mu sync.RWMutex
28-
m map[*Authenticator]time.Time
29-
earliest time.Time
27+
mu sync.RWMutex
28+
m map[*Authenticator]time.Time
29+
metricSet func(*time.Time)
3030
}
3131

32-
var expirationMetrics = &certificateExpirationTracker{m: map[*Authenticator]time.Time{}}
32+
var expirationMetrics = &certificateExpirationTracker{
33+
m: map[*Authenticator]time.Time{},
34+
metricSet: func(e *time.Time) {
35+
metrics.ClientCertExpiry.Set(e)
36+
},
37+
}
3338

34-
// set stores the given expiration time and updates the updates earliest.
39+
// set stores the given expiration time and updates the updates the certificate
40+
// expiry metric to the earliest expiration time.
3541
func (c *certificateExpirationTracker) set(a *Authenticator, t time.Time) {
3642
c.mu.Lock()
3743
defer c.mu.Unlock()
3844
c.m[a] = t
3945

40-
// update earliest
4146
earliest := time.Time{}
4247
for _, t := range c.m {
4348
if t.IsZero() {
@@ -47,18 +52,9 @@ func (c *certificateExpirationTracker) set(a *Authenticator, t time.Time) {
4752
earliest = t
4853
}
4954
}
50-
c.earliest = earliest
51-
}
52-
53-
// report reports the ttl to the earliest reported expiration time.
54-
// If no Authenticators have reported a certificate expiration, this reports nil.
55-
func (c *certificateExpirationTracker) report(now func() time.Time) {
56-
c.mu.RLock()
57-
defer c.mu.RUnlock()
58-
if c.earliest.IsZero() {
59-
metrics.ClientCertTTL.Set(nil)
55+
if earliest.IsZero() {
56+
c.metricSet(nil)
6057
} else {
61-
ttl := c.earliest.Sub(now())
62-
metrics.ClientCertTTL.Set(&ttl)
58+
c.metricSet(&earliest)
6359
}
6460
}

staging/src/k8s.io/client-go/plugin/pkg/client/auth/exec/metrics_test.go

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,36 +19,27 @@ package exec
1919
import (
2020
"testing"
2121
"time"
22-
23-
"k8s.io/client-go/tools/metrics"
2422
)
2523

26-
type mockTTLGauge struct {
27-
v *time.Duration
24+
type mockExpiryGauge struct {
25+
v *time.Time
2826
}
2927

30-
func (m *mockTTLGauge) Set(d *time.Duration) {
31-
m.v = d
28+
func (m *mockExpiryGauge) Set(t *time.Time) {
29+
m.v = t
3230
}
3331

34-
func ptr(d time.Duration) *time.Duration {
35-
return &d
32+
func ptr(t time.Time) *time.Time {
33+
return &t
3634
}
3735

3836
func TestCertificateExpirationTracker(t *testing.T) {
3937
now := time.Now()
40-
nowFn := func() time.Time { return now }
41-
mockMetric := &mockTTLGauge{}
42-
realMetric := metrics.ClientCertTTL
43-
metrics.ClientCertTTL = mockMetric
44-
defer func() {
45-
metrics.ClientCertTTL = realMetric
46-
}()
38+
mockMetric := &mockExpiryGauge{}
4739

48-
tracker := &certificateExpirationTracker{m: map[*Authenticator]time.Time{}}
49-
tracker.report(nowFn)
50-
if mockMetric.v != nil {
51-
t.Error("empty tracker should record nil value")
40+
tracker := &certificateExpirationTracker{
41+
m: map[*Authenticator]time.Time{},
42+
metricSet: mockMetric.Set,
5243
}
5344

5445
firstAuthenticator := &Authenticator{}
@@ -57,31 +48,31 @@ func TestCertificateExpirationTracker(t *testing.T) {
5748
desc string
5849
auth *Authenticator
5950
time time.Time
60-
want *time.Duration
51+
want *time.Time
6152
}{
6253
{
6354
desc: "ttl for one authenticator",
6455
auth: firstAuthenticator,
6556
time: now.Add(time.Minute * 10),
66-
want: ptr(time.Minute * 10),
57+
want: ptr(now.Add(time.Minute * 10)),
6758
},
6859
{
6960
desc: "second authenticator shorter ttl",
7061
auth: secondAuthenticator,
7162
time: now.Add(time.Minute * 5),
72-
want: ptr(time.Minute * 5),
63+
want: ptr(now.Add(time.Minute * 5)),
7364
},
7465
{
7566
desc: "update shorter to be longer",
7667
auth: secondAuthenticator,
7768
time: now.Add(time.Minute * 15),
78-
want: ptr(time.Minute * 10),
69+
want: ptr(now.Add(time.Minute * 10)),
7970
},
8071
{
8172
desc: "update shorter to be zero time",
8273
auth: firstAuthenticator,
8374
time: time.Time{},
84-
want: ptr(time.Minute * 15),
75+
want: ptr(now.Add(time.Minute * 15)),
8576
},
8677
{
8778
desc: "update last to be zero time records nil",
@@ -93,13 +84,12 @@ func TestCertificateExpirationTracker(t *testing.T) {
9384
// Must run in series as the tests build off each other.
9485
t.Run(tc.desc, func(t *testing.T) {
9586
tracker.set(tc.auth, tc.time)
96-
tracker.report(nowFn)
9787
if mockMetric.v != nil && tc.want != nil {
98-
if mockMetric.v.Seconds() != tc.want.Seconds() {
99-
t.Errorf("got: %v; want: %v", mockMetric.v, tc.want)
88+
if !mockMetric.v.Equal(*tc.want) {
89+
t.Errorf("got: %s; want: %s", mockMetric.v, tc.want)
10090
}
10191
} else if mockMetric.v != tc.want {
102-
t.Errorf("got: %v; want: %v", mockMetric.v, tc.want)
92+
t.Errorf("got: %s; want: %s", mockMetric.v, tc.want)
10393
}
10494
})
10595
}

staging/src/k8s.io/client-go/tools/metrics/metrics.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ type DurationMetric interface {
3131
Observe(duration time.Duration)
3232
}
3333

34-
// TTLMetric sets the time to live of something.
35-
type TTLMetric interface {
36-
Set(ttl *time.Duration)
34+
// ExpiryMetric sets some time of expiry. If nil, assume not relevant.
35+
type ExpiryMetric interface {
36+
Set(expiry *time.Time)
3737
}
3838

3939
// LatencyMetric observes client latency partitioned by verb and url.
@@ -47,8 +47,8 @@ type ResultMetric interface {
4747
}
4848

4949
var (
50-
// ClientCertTTL is the time to live of a client certificate
51-
ClientCertTTL TTLMetric = noopTTL{}
50+
// ClientCertExpiry is the expiry time of a client certificate
51+
ClientCertExpiry ExpiryMetric = noopExpiry{}
5252
// ClientCertRotationAge is the age of a certificate that has just been rotated.
5353
ClientCertRotationAge DurationMetric = noopDuration{}
5454
// RequestLatency is the latency metric that rest clients will update.
@@ -59,7 +59,7 @@ var (
5959

6060
// RegisterOpts contains all the metrics to register. Metrics may be nil.
6161
type RegisterOpts struct {
62-
ClientCertTTL TTLMetric
62+
ClientCertExpiry ExpiryMetric
6363
ClientCertRotationAge DurationMetric
6464
RequestLatency LatencyMetric
6565
RequestResult ResultMetric
@@ -69,8 +69,8 @@ type RegisterOpts struct {
6969
// only be called once.
7070
func Register(opts RegisterOpts) {
7171
registerMetrics.Do(func() {
72-
if opts.ClientCertTTL != nil {
73-
ClientCertTTL = opts.ClientCertTTL
72+
if opts.ClientCertExpiry != nil {
73+
ClientCertExpiry = opts.ClientCertExpiry
7474
}
7575
if opts.ClientCertRotationAge != nil {
7676
ClientCertRotationAge = opts.ClientCertRotationAge
@@ -88,9 +88,9 @@ type noopDuration struct{}
8888

8989
func (noopDuration) Observe(time.Duration) {}
9090

91-
type noopTTL struct{}
91+
type noopExpiry struct{}
9292

93-
func (noopTTL) Set(*time.Duration) {}
93+
func (noopExpiry) Set(*time.Time) {}
9494

9595
type noopLatency struct{}
9696

staging/src/k8s.io/client-go/util/certificate/certificate_manager.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,6 @@ type Config struct {
112112
// initialized using a generic, multi-use cert/key pair which will be
113113
// quickly replaced with a unique cert/key pair.
114114
BootstrapKeyPEM []byte
115-
// CertificateExpiration will record a metric that shows the remaining
116-
// lifetime of the certificate. This metric is a gauge because only the
117-
// current cert expiry time is really useful. Reading this metric at any
118-
// time simply gives the next expiration date, no need to keep some
119-
// history (histogram) of all previous expiry dates.
120-
CertificateExpiration Gauge
121115
// CertificateRotation will record a metric showing the time in seconds
122116
// that certificates lived before being rotated. This metric is a histogram
123117
// because there is value in keeping a history of rotation cadences. It
@@ -185,7 +179,6 @@ type manager struct {
185179

186180
certStore Store
187181

188-
certificateExpiration Gauge
189182
certificateRotation Histogram
190183
certificateRenewFailure Counter
191184

@@ -230,7 +223,6 @@ func NewManager(config *Config) (Manager, error) {
230223
certStore: config.CertificateStore,
231224
cert: cert,
232225
forceRotation: forceRotation,
233-
certificateExpiration: config.CertificateExpiration,
234226
certificateRotation: config.CertificateRotation,
235227
certificateRenewFailure: config.CertificateRenewFailure,
236228
now: time.Now,
@@ -554,9 +546,6 @@ func (m *manager) nextRotationDeadline() time.Time {
554546
deadline := m.cert.Leaf.NotBefore.Add(jitteryDuration(totalDuration))
555547

556548
klog.V(2).Infof("Certificate expiration is %v, rotation deadline is %v", notAfter, deadline)
557-
if m.certificateExpiration != nil {
558-
m.certificateExpiration.Set(float64(notAfter.Unix()))
559-
}
560549
return deadline
561550
}
562551

staging/src/k8s.io/client-go/util/certificate/certificate_manager_test.go

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -200,18 +200,16 @@ func TestSetRotationDeadline(t *testing.T) {
200200

201201
for _, tc := range testCases {
202202
t.Run(tc.name, func(t *testing.T) {
203-
g := metricMock{}
204203
m := manager{
205204
cert: &tls.Certificate{
206205
Leaf: &x509.Certificate{
207206
NotBefore: tc.notBefore,
208207
NotAfter: tc.notAfter,
209208
},
210209
},
211-
getTemplate: func() *x509.CertificateRequest { return &x509.CertificateRequest{} },
212-
usages: []certificates.KeyUsage{},
213-
certificateExpiration: &g,
214-
now: func() time.Time { return now },
210+
getTemplate: func() *x509.CertificateRequest { return &x509.CertificateRequest{} },
211+
usages: []certificates.KeyUsage{},
212+
now: func() time.Time { return now },
215213
}
216214
jitteryDuration = func(float64) time.Duration { return time.Duration(float64(tc.notAfter.Sub(tc.notBefore)) * 0.7) }
217215
lowerBound := tc.notBefore.Add(time.Duration(float64(tc.notAfter.Sub(tc.notBefore)) * 0.7))
@@ -225,12 +223,6 @@ func TestSetRotationDeadline(t *testing.T) {
225223
deadline,
226224
lowerBound)
227225
}
228-
if g.calls != 1 {
229-
t.Errorf("%d metrics were recorded, wanted %d", g.calls, 1)
230-
}
231-
if g.lastValue != float64(tc.notAfter.Unix()) {
232-
t.Errorf("%f value for metric was recorded, wanted %d", g.lastValue, tc.notAfter.Unix())
233-
}
234226
})
235227
}
236228
}

0 commit comments

Comments
 (0)