Skip to content

Commit 09093f2

Browse files
authored
Merge pull request kubernetes#123793 from aramase/aramase/f/authn_config_reload_metrics
Add metrics for authentication config reload
2 parents 77ecfb7 + 62ac88b commit 09093f2

File tree

5 files changed

+265
-3
lines changed

5 files changed

+265
-3
lines changed

pkg/controlplane/apiserver/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ func BuildGenericConfig(
147147
ctx := wait.ContextForChannel(genericConfig.DrainedNotify())
148148

149149
// Authentication.ApplyTo requires already applied OpenAPIConfig and EgressSelector if present
150-
if lastErr = s.Authentication.ApplyTo(ctx, &genericConfig.Authentication, genericConfig.SecureServing, genericConfig.EgressSelector, genericConfig.OpenAPIConfig, genericConfig.OpenAPIV3Config, clientgoExternalClient, versionedInformers); lastErr != nil {
150+
if lastErr = s.Authentication.ApplyTo(ctx, &genericConfig.Authentication, genericConfig.SecureServing, genericConfig.EgressSelector, genericConfig.OpenAPIConfig, genericConfig.OpenAPIV3Config, clientgoExternalClient, versionedInformers, genericConfig.APIServerID); lastErr != nil {
151151
return
152152
}
153153

pkg/kubeapiserver/options/authentication.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
genericapiserver "k8s.io/apiserver/pkg/server"
4242
"k8s.io/apiserver/pkg/server/egressselector"
4343
genericoptions "k8s.io/apiserver/pkg/server/options"
44+
authenticationconfigmetrics "k8s.io/apiserver/pkg/server/options/authenticationconfig/metrics"
4445
utilfeature "k8s.io/apiserver/pkg/util/feature"
4546
"k8s.io/apiserver/plugin/pkg/authenticator/token/oidc"
4647
"k8s.io/client-go/informers"
@@ -588,7 +589,16 @@ func (o *BuiltInAuthenticationOptions) ToAuthenticationConfig() (kubeauthenticat
588589

589590
// ApplyTo requires already applied OpenAPIConfig and EgressSelector if present.
590591
// The input context controls the lifecycle of background goroutines started to reload the authentication config file.
591-
func (o *BuiltInAuthenticationOptions) ApplyTo(ctx context.Context, authInfo *genericapiserver.AuthenticationInfo, secureServing *genericapiserver.SecureServingInfo, egressSelector *egressselector.EgressSelector, openAPIConfig *openapicommon.Config, openAPIV3Config *openapicommon.OpenAPIV3Config, extclient kubernetes.Interface, versionedInformer informers.SharedInformerFactory) error {
592+
func (o *BuiltInAuthenticationOptions) ApplyTo(
593+
ctx context.Context,
594+
authInfo *genericapiserver.AuthenticationInfo,
595+
secureServing *genericapiserver.SecureServingInfo,
596+
egressSelector *egressselector.EgressSelector,
597+
openAPIConfig *openapicommon.Config,
598+
openAPIV3Config *openapicommon.OpenAPIV3Config,
599+
extclient kubernetes.Interface,
600+
versionedInformer informers.SharedInformerFactory,
601+
apiServerID string) error {
592602
if o == nil {
593603
return nil
594604
}
@@ -654,14 +664,14 @@ func (o *BuiltInAuthenticationOptions) ApplyTo(ctx context.Context, authInfo *ge
654664
authInfo.Authenticator = authenticator
655665

656666
if len(o.AuthenticationConfigFile) > 0 {
667+
authenticationconfigmetrics.RegisterMetrics()
657668
trackedAuthenticationConfigData := authenticatorConfig.AuthenticationConfigData
658669
var mu sync.Mutex
659670
go filesystem.WatchUntil(
660671
ctx,
661672
time.Minute,
662673
o.AuthenticationConfigFile,
663674
func() {
664-
// TODO add metrics
665675
// TODO collapse onto shared logic with DynamicEncryptionConfigContent controller
666676

667677
mu.Lock()
@@ -670,6 +680,7 @@ func (o *BuiltInAuthenticationOptions) ApplyTo(ctx context.Context, authInfo *ge
670680
authConfigBytes, err := os.ReadFile(o.AuthenticationConfigFile)
671681
if err != nil {
672682
klog.ErrorS(err, "failed to read authentication config file")
683+
authenticationconfigmetrics.RecordAuthenticationConfigAutomaticReloadFailure(apiServerID)
673684
// we do not update the tracker here because this error could eventually resolve as we keep retrying
674685
return
675686
}
@@ -683,13 +694,15 @@ func (o *BuiltInAuthenticationOptions) ApplyTo(ctx context.Context, authInfo *ge
683694
authConfig, err := loadAuthenticationConfigFromData(authConfigBytes)
684695
if err != nil {
685696
klog.ErrorS(err, "failed to load authentication config")
697+
authenticationconfigmetrics.RecordAuthenticationConfigAutomaticReloadFailure(apiServerID)
686698
// this config is not structurally valid and never will be, update the tracker so we stop retrying
687699
trackedAuthenticationConfigData = authConfigData
688700
return
689701
}
690702

691703
if err := apiservervalidation.ValidateAuthenticationConfiguration(authConfig, authenticatorConfig.ServiceAccountIssuers).ToAggregate(); err != nil {
692704
klog.ErrorS(err, "failed to validate authentication config")
705+
authenticationconfigmetrics.RecordAuthenticationConfigAutomaticReloadFailure(apiServerID)
693706
// this config is not semantically valid and never will be, update the tracker so we stop retrying
694707
trackedAuthenticationConfigData = authConfigData
695708
return
@@ -699,11 +712,14 @@ func (o *BuiltInAuthenticationOptions) ApplyTo(ctx context.Context, authInfo *ge
699712
defer timeoutCancel()
700713
if err := updateAuthenticationConfig(timeoutCtx, authConfig); err != nil {
701714
klog.ErrorS(err, "failed to update authentication config")
715+
authenticationconfigmetrics.RecordAuthenticationConfigAutomaticReloadFailure(apiServerID)
702716
// we do not update the tracker here because this error could eventually resolve as we keep retrying
703717
return
704718
}
705719

706720
trackedAuthenticationConfigData = authConfigData
721+
klog.InfoS("reloaded authentication config")
722+
authenticationconfigmetrics.RecordAuthenticationConfigAutomaticReloadSuccess(apiServerID)
707723
},
708724
func(err error) { klog.ErrorS(err, "watching authentication config file") },
709725
)
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package metrics
18+
19+
import (
20+
"crypto/sha256"
21+
"fmt"
22+
"sync"
23+
24+
"k8s.io/component-base/metrics"
25+
"k8s.io/component-base/metrics/legacyregistry"
26+
)
27+
28+
const (
29+
namespace = "apiserver"
30+
subsystem = "authentication_config_controller"
31+
)
32+
33+
var (
34+
authenticationConfigAutomaticReloadsTotal = metrics.NewCounterVec(
35+
&metrics.CounterOpts{
36+
Namespace: namespace,
37+
Subsystem: subsystem,
38+
Name: "automatic_reloads_total",
39+
Help: "Total number of automatic reloads of authentication configuration split by status and apiserver identity.",
40+
StabilityLevel: metrics.ALPHA,
41+
},
42+
[]string{"status", "apiserver_id_hash"},
43+
)
44+
45+
authenticationConfigAutomaticReloadLastTimestampSeconds = metrics.NewGaugeVec(
46+
&metrics.GaugeOpts{
47+
Namespace: namespace,
48+
Subsystem: subsystem,
49+
Name: "automatic_reload_last_timestamp_seconds",
50+
Help: "Timestamp of the last automatic reload of authentication configuration split by status and apiserver identity.",
51+
StabilityLevel: metrics.ALPHA,
52+
},
53+
[]string{"status", "apiserver_id_hash"},
54+
)
55+
)
56+
57+
var registerMetrics sync.Once
58+
59+
func RegisterMetrics() {
60+
registerMetrics.Do(func() {
61+
legacyregistry.MustRegister(authenticationConfigAutomaticReloadsTotal)
62+
legacyregistry.MustRegister(authenticationConfigAutomaticReloadLastTimestampSeconds)
63+
})
64+
}
65+
66+
func ResetMetricsForTest() {
67+
authenticationConfigAutomaticReloadsTotal.Reset()
68+
authenticationConfigAutomaticReloadLastTimestampSeconds.Reset()
69+
}
70+
71+
func RecordAuthenticationConfigAutomaticReloadFailure(apiServerID string) {
72+
apiServerIDHash := getHash(apiServerID)
73+
authenticationConfigAutomaticReloadsTotal.WithLabelValues("failure", apiServerIDHash).Inc()
74+
authenticationConfigAutomaticReloadLastTimestampSeconds.WithLabelValues("failure", apiServerIDHash).SetToCurrentTime()
75+
}
76+
77+
func RecordAuthenticationConfigAutomaticReloadSuccess(apiServerID string) {
78+
apiServerIDHash := getHash(apiServerID)
79+
authenticationConfigAutomaticReloadsTotal.WithLabelValues("success", apiServerIDHash).Inc()
80+
authenticationConfigAutomaticReloadLastTimestampSeconds.WithLabelValues("success", apiServerIDHash).SetToCurrentTime()
81+
}
82+
83+
func getHash(data string) string {
84+
if len(data) == 0 {
85+
return ""
86+
}
87+
return fmt.Sprintf("sha256:%x", sha256.Sum256([]byte(data)))
88+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package metrics
18+
19+
import (
20+
"strings"
21+
"testing"
22+
23+
"k8s.io/component-base/metrics/legacyregistry"
24+
"k8s.io/component-base/metrics/testutil"
25+
)
26+
27+
const (
28+
testAPIServerID = "testAPIServerID"
29+
testAPIServerIDHash = "sha256:14f9d63e669337ac6bfda2e2162915ee6a6067743eddd4e5c374b572f951ff37"
30+
)
31+
32+
func TestRecordAuthenticationConfigAutomaticReloadFailure(t *testing.T) {
33+
expectedValue := `
34+
# HELP apiserver_authentication_config_controller_automatic_reloads_total [ALPHA] Total number of automatic reloads of authentication configuration split by status and apiserver identity.
35+
# TYPE apiserver_authentication_config_controller_automatic_reloads_total counter
36+
apiserver_authentication_config_controller_automatic_reloads_total {apiserver_id_hash="sha256:14f9d63e669337ac6bfda2e2162915ee6a6067743eddd4e5c374b572f951ff37",status="failure"} 1
37+
`
38+
metrics := []string{
39+
namespace + "_" + subsystem + "_automatic_reloads_total",
40+
}
41+
42+
authenticationConfigAutomaticReloadsTotal.Reset()
43+
RegisterMetrics()
44+
45+
RecordAuthenticationConfigAutomaticReloadFailure(testAPIServerID)
46+
if err := testutil.GatherAndCompare(legacyregistry.DefaultGatherer, strings.NewReader(expectedValue), metrics...); err != nil {
47+
t.Fatal(err)
48+
}
49+
}
50+
51+
func TestRecordAuthenticationConfigAutomaticReloadSuccess(t *testing.T) {
52+
expectedValue := `
53+
# HELP apiserver_authentication_config_controller_automatic_reloads_total [ALPHA] Total number of automatic reloads of authentication configuration split by status and apiserver identity.
54+
# TYPE apiserver_authentication_config_controller_automatic_reloads_total counter
55+
apiserver_authentication_config_controller_automatic_reloads_total {apiserver_id_hash="sha256:14f9d63e669337ac6bfda2e2162915ee6a6067743eddd4e5c374b572f951ff37",status="success"} 1
56+
`
57+
metrics := []string{
58+
namespace + "_" + subsystem + "_automatic_reloads_total",
59+
}
60+
61+
authenticationConfigAutomaticReloadsTotal.Reset()
62+
RegisterMetrics()
63+
64+
RecordAuthenticationConfigAutomaticReloadSuccess(testAPIServerID)
65+
if err := testutil.GatherAndCompare(legacyregistry.DefaultGatherer, strings.NewReader(expectedValue), metrics...); err != nil {
66+
t.Fatal(err)
67+
}
68+
}
69+
70+
func TestAuthenticationConfigAutomaticReloadLastTimestampSeconds(t *testing.T) {
71+
testCases := []struct {
72+
expectedValue string
73+
resultLabel string
74+
timestamp int64
75+
}{
76+
{
77+
expectedValue: `
78+
# HELP apiserver_authentication_config_controller_automatic_reload_last_timestamp_seconds [ALPHA] Timestamp of the last automatic reload of authentication configuration split by status and apiserver identity.
79+
# TYPE apiserver_authentication_config_controller_automatic_reload_last_timestamp_seconds gauge
80+
apiserver_authentication_config_controller_automatic_reload_last_timestamp_seconds{apiserver_id_hash="sha256:14f9d63e669337ac6bfda2e2162915ee6a6067743eddd4e5c374b572f951ff37",status="failure"} 1.689101941e+09
81+
`,
82+
resultLabel: "failure",
83+
timestamp: 1689101941,
84+
},
85+
{
86+
expectedValue: `
87+
# HELP apiserver_authentication_config_controller_automatic_reload_last_timestamp_seconds [ALPHA] Timestamp of the last automatic reload of authentication configuration split by status and apiserver identity.
88+
# TYPE apiserver_authentication_config_controller_automatic_reload_last_timestamp_seconds gauge
89+
apiserver_authentication_config_controller_automatic_reload_last_timestamp_seconds{apiserver_id_hash="sha256:14f9d63e669337ac6bfda2e2162915ee6a6067743eddd4e5c374b572f951ff37",status="success"} 1.689101941e+09
90+
`,
91+
resultLabel: "success",
92+
timestamp: 1689101941,
93+
},
94+
}
95+
96+
metrics := []string{
97+
namespace + "_" + subsystem + "_automatic_reload_last_timestamp_seconds",
98+
}
99+
RegisterMetrics()
100+
101+
for _, tc := range testCases {
102+
authenticationConfigAutomaticReloadLastTimestampSeconds.Reset()
103+
authenticationConfigAutomaticReloadLastTimestampSeconds.WithLabelValues(tc.resultLabel, testAPIServerIDHash).Set(float64(tc.timestamp))
104+
105+
if err := testutil.GatherAndCompare(legacyregistry.DefaultGatherer, strings.NewReader(tc.expectedValue), metrics...); err != nil {
106+
t.Fatal(err)
107+
}
108+
}
109+
}

0 commit comments

Comments
 (0)