Add allowed/denied metrics for authorizers
This commit is contained in:
		@@ -32,6 +32,7 @@ import (
 | 
				
			|||||||
	"k8s.io/apiserver/pkg/authentication/user"
 | 
						"k8s.io/apiserver/pkg/authentication/user"
 | 
				
			||||||
	"k8s.io/apiserver/pkg/authorization/authorizer"
 | 
						"k8s.io/apiserver/pkg/authorization/authorizer"
 | 
				
			||||||
	"k8s.io/apiserver/pkg/authorization/authorizerfactory"
 | 
						"k8s.io/apiserver/pkg/authorization/authorizerfactory"
 | 
				
			||||||
 | 
						authorizationmetrics "k8s.io/apiserver/pkg/authorization/metrics"
 | 
				
			||||||
	"k8s.io/apiserver/pkg/authorization/union"
 | 
						"k8s.io/apiserver/pkg/authorization/union"
 | 
				
			||||||
	"k8s.io/apiserver/pkg/server/options/authorizationconfig/metrics"
 | 
						"k8s.io/apiserver/pkg/server/options/authorizationconfig/metrics"
 | 
				
			||||||
	webhookutil "k8s.io/apiserver/pkg/util/webhook"
 | 
						webhookutil "k8s.io/apiserver/pkg/util/webhook"
 | 
				
			||||||
@@ -101,21 +102,21 @@ func (r *reloadableAuthorizerResolver) newForConfig(authzConfig *authzconfig.Aut
 | 
				
			|||||||
			if r.nodeAuthorizer == nil {
 | 
								if r.nodeAuthorizer == nil {
 | 
				
			||||||
				return nil, nil, fmt.Errorf("authorizer type Node is not allowed if it was not enabled at initial server startup")
 | 
									return nil, nil, fmt.Errorf("authorizer type Node is not allowed if it was not enabled at initial server startup")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			authorizers = append(authorizers, r.nodeAuthorizer)
 | 
								authorizers = append(authorizers, authorizationmetrics.InstrumentedAuthorizer(string(configuredAuthorizer.Type), configuredAuthorizer.Name, r.nodeAuthorizer))
 | 
				
			||||||
			ruleResolvers = append(ruleResolvers, r.nodeAuthorizer)
 | 
								ruleResolvers = append(ruleResolvers, r.nodeAuthorizer)
 | 
				
			||||||
		case authzconfig.AuthorizerType(modes.ModeAlwaysAllow):
 | 
							case authzconfig.AuthorizerType(modes.ModeAlwaysAllow):
 | 
				
			||||||
			alwaysAllowAuthorizer := authorizerfactory.NewAlwaysAllowAuthorizer()
 | 
								alwaysAllowAuthorizer := authorizerfactory.NewAlwaysAllowAuthorizer()
 | 
				
			||||||
			authorizers = append(authorizers, alwaysAllowAuthorizer)
 | 
								authorizers = append(authorizers, authorizationmetrics.InstrumentedAuthorizer(string(configuredAuthorizer.Type), configuredAuthorizer.Name, alwaysAllowAuthorizer))
 | 
				
			||||||
			ruleResolvers = append(ruleResolvers, alwaysAllowAuthorizer)
 | 
								ruleResolvers = append(ruleResolvers, alwaysAllowAuthorizer)
 | 
				
			||||||
		case authzconfig.AuthorizerType(modes.ModeAlwaysDeny):
 | 
							case authzconfig.AuthorizerType(modes.ModeAlwaysDeny):
 | 
				
			||||||
			alwaysDenyAuthorizer := authorizerfactory.NewAlwaysDenyAuthorizer()
 | 
								alwaysDenyAuthorizer := authorizerfactory.NewAlwaysDenyAuthorizer()
 | 
				
			||||||
			authorizers = append(authorizers, alwaysDenyAuthorizer)
 | 
								authorizers = append(authorizers, authorizationmetrics.InstrumentedAuthorizer(string(configuredAuthorizer.Type), configuredAuthorizer.Name, alwaysDenyAuthorizer))
 | 
				
			||||||
			ruleResolvers = append(ruleResolvers, alwaysDenyAuthorizer)
 | 
								ruleResolvers = append(ruleResolvers, alwaysDenyAuthorizer)
 | 
				
			||||||
		case authzconfig.AuthorizerType(modes.ModeABAC):
 | 
							case authzconfig.AuthorizerType(modes.ModeABAC):
 | 
				
			||||||
			if r.abacAuthorizer == nil {
 | 
								if r.abacAuthorizer == nil {
 | 
				
			||||||
				return nil, nil, fmt.Errorf("authorizer type ABAC is not allowed if it was not enabled at initial server startup")
 | 
									return nil, nil, fmt.Errorf("authorizer type ABAC is not allowed if it was not enabled at initial server startup")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			authorizers = append(authorizers, r.abacAuthorizer)
 | 
								authorizers = append(authorizers, authorizationmetrics.InstrumentedAuthorizer(string(configuredAuthorizer.Type), configuredAuthorizer.Name, r.abacAuthorizer))
 | 
				
			||||||
			ruleResolvers = append(ruleResolvers, r.abacAuthorizer)
 | 
								ruleResolvers = append(ruleResolvers, r.abacAuthorizer)
 | 
				
			||||||
		case authzconfig.AuthorizerType(modes.ModeWebhook):
 | 
							case authzconfig.AuthorizerType(modes.ModeWebhook):
 | 
				
			||||||
			if r.initialConfig.WebhookRetryBackoff == nil {
 | 
								if r.initialConfig.WebhookRetryBackoff == nil {
 | 
				
			||||||
@@ -145,13 +146,13 @@ func (r *reloadableAuthorizerResolver) newForConfig(authzConfig *authzconfig.Aut
 | 
				
			|||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return nil, nil, err
 | 
									return nil, nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			authorizers = append(authorizers, webhookAuthorizer)
 | 
								authorizers = append(authorizers, authorizationmetrics.InstrumentedAuthorizer(string(configuredAuthorizer.Type), configuredAuthorizer.Name, webhookAuthorizer))
 | 
				
			||||||
			ruleResolvers = append(ruleResolvers, webhookAuthorizer)
 | 
								ruleResolvers = append(ruleResolvers, webhookAuthorizer)
 | 
				
			||||||
		case authzconfig.AuthorizerType(modes.ModeRBAC):
 | 
							case authzconfig.AuthorizerType(modes.ModeRBAC):
 | 
				
			||||||
			if r.rbacAuthorizer == nil {
 | 
								if r.rbacAuthorizer == nil {
 | 
				
			||||||
				return nil, nil, fmt.Errorf("authorizer type RBAC is not allowed if it was not enabled at initial server startup")
 | 
									return nil, nil, fmt.Errorf("authorizer type RBAC is not allowed if it was not enabled at initial server startup")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			authorizers = append(authorizers, r.rbacAuthorizer)
 | 
								authorizers = append(authorizers, authorizationmetrics.InstrumentedAuthorizer(string(configuredAuthorizer.Type), configuredAuthorizer.Name, r.rbacAuthorizer))
 | 
				
			||||||
			ruleResolvers = append(ruleResolvers, r.rbacAuthorizer)
 | 
								ruleResolvers = append(ruleResolvers, r.rbacAuthorizer)
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			return nil, nil, fmt.Errorf("unknown authorization mode %s specified", configuredAuthorizer.Type)
 | 
								return nil, nil, fmt.Errorf("unknown authorization mode %s specified", configuredAuthorizer.Type)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,92 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2024 The Kubernetes Authors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package metrics
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/apiserver/pkg/authorization/authorizer"
 | 
				
			||||||
 | 
						"k8s.io/component-base/metrics"
 | 
				
			||||||
 | 
						"k8s.io/component-base/metrics/legacyregistry"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						namespace = "apiserver"
 | 
				
			||||||
 | 
						subsystem = "authorization"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						authorizationDecisionsTotal = metrics.NewCounterVec(
 | 
				
			||||||
 | 
							&metrics.CounterOpts{
 | 
				
			||||||
 | 
								Namespace:      namespace,
 | 
				
			||||||
 | 
								Subsystem:      subsystem,
 | 
				
			||||||
 | 
								Name:           "decisions_total",
 | 
				
			||||||
 | 
								Help:           "Total number of terminal decisions made by an authorizer split by authorizer type, name, and decision.",
 | 
				
			||||||
 | 
								StabilityLevel: metrics.ALPHA,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							[]string{"type", "name", "decision"},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var registerMetrics sync.Once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func RegisterMetrics() {
 | 
				
			||||||
 | 
						registerMetrics.Do(func() {
 | 
				
			||||||
 | 
							legacyregistry.MustRegister(authorizationDecisionsTotal)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ResetMetricsForTest() {
 | 
				
			||||||
 | 
						authorizationDecisionsTotal.Reset()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func RecordAuthorizationDecision(authorizerType, authorizerName, decision string) {
 | 
				
			||||||
 | 
						authorizationDecisionsTotal.WithLabelValues(authorizerType, authorizerName, decision).Inc()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func InstrumentedAuthorizer(authorizerType string, authorizerName string, delegate authorizer.Authorizer) authorizer.Authorizer {
 | 
				
			||||||
 | 
						RegisterMetrics()
 | 
				
			||||||
 | 
						return &instrumentedAuthorizer{
 | 
				
			||||||
 | 
							authorizerType: string(authorizerType),
 | 
				
			||||||
 | 
							authorizerName: authorizerName,
 | 
				
			||||||
 | 
							delegate:       delegate,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type instrumentedAuthorizer struct {
 | 
				
			||||||
 | 
						authorizerType string
 | 
				
			||||||
 | 
						authorizerName string
 | 
				
			||||||
 | 
						delegate       authorizer.Authorizer
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *instrumentedAuthorizer) Authorize(ctx context.Context, attributes authorizer.Attributes) (authorizer.Decision, string, error) {
 | 
				
			||||||
 | 
						decision, reason, err := a.delegate.Authorize(ctx, attributes)
 | 
				
			||||||
 | 
						switch decision {
 | 
				
			||||||
 | 
						case authorizer.DecisionNoOpinion:
 | 
				
			||||||
 | 
							// non-terminal, not reported
 | 
				
			||||||
 | 
						case authorizer.DecisionAllow:
 | 
				
			||||||
 | 
							// matches SubjectAccessReview status.allowed field name
 | 
				
			||||||
 | 
							RecordAuthorizationDecision(a.authorizerType, a.authorizerName, "allowed")
 | 
				
			||||||
 | 
						case authorizer.DecisionDeny:
 | 
				
			||||||
 | 
							// matches SubjectAccessReview status.denied field name
 | 
				
			||||||
 | 
							RecordAuthorizationDecision(a.authorizerType, a.authorizerName, "denied")
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							RecordAuthorizationDecision(a.authorizerType, a.authorizerName, "unknown")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return decision, reason, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,105 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2024 The Kubernetes Authors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package metrics
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/apiserver/pkg/authorization/authorizer"
 | 
				
			||||||
 | 
						"k8s.io/component-base/metrics/legacyregistry"
 | 
				
			||||||
 | 
						"k8s.io/component-base/metrics/testutil"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRecordAuthorizationDecisionsTotal(t *testing.T) {
 | 
				
			||||||
 | 
						prefix := `
 | 
				
			||||||
 | 
					    # HELP apiserver_authorization_decisions_total [ALPHA] Total number of terminal decisions made by an authorizer split by authorizer type, name, and decision.
 | 
				
			||||||
 | 
					    # TYPE apiserver_authorization_decisions_total counter`
 | 
				
			||||||
 | 
						metrics := []string{
 | 
				
			||||||
 | 
							namespace + "_" + subsystem + "_decisions_total",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						authorizationDecisionsTotal.Reset()
 | 
				
			||||||
 | 
						RegisterMetrics()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dummyAuthorizer := &dummyAuthorizer{}
 | 
				
			||||||
 | 
						a := InstrumentedAuthorizer("mytype", "myname", dummyAuthorizer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// allow
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							dummyAuthorizer.decision = authorizer.DecisionAllow
 | 
				
			||||||
 | 
							_, _, _ = a.Authorize(context.Background(), nil)
 | 
				
			||||||
 | 
							expectedValue := prefix + `
 | 
				
			||||||
 | 
								apiserver_authorization_decisions_total{decision="allowed",name="myname",type="mytype"} 1
 | 
				
			||||||
 | 
							`
 | 
				
			||||||
 | 
							if err := testutil.GatherAndCompare(legacyregistry.DefaultGatherer, strings.NewReader(expectedValue), metrics...); err != nil {
 | 
				
			||||||
 | 
								t.Fatal(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							authorizationDecisionsTotal.Reset()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// deny
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							dummyAuthorizer.decision = authorizer.DecisionDeny
 | 
				
			||||||
 | 
							_, _, _ = a.Authorize(context.Background(), nil)
 | 
				
			||||||
 | 
							_, _, _ = a.Authorize(context.Background(), nil)
 | 
				
			||||||
 | 
							expectedValue := prefix + `
 | 
				
			||||||
 | 
								apiserver_authorization_decisions_total{decision="denied",name="myname",type="mytype"} 2
 | 
				
			||||||
 | 
							`
 | 
				
			||||||
 | 
							if err := testutil.GatherAndCompare(legacyregistry.DefaultGatherer, strings.NewReader(expectedValue), metrics...); err != nil {
 | 
				
			||||||
 | 
								t.Fatal(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							authorizationDecisionsTotal.Reset()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// no-opinion emits no metric
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							dummyAuthorizer.decision = authorizer.DecisionNoOpinion
 | 
				
			||||||
 | 
							_, _, _ = a.Authorize(context.Background(), nil)
 | 
				
			||||||
 | 
							_, _, _ = a.Authorize(context.Background(), nil)
 | 
				
			||||||
 | 
							expectedValue := prefix + `
 | 
				
			||||||
 | 
							`
 | 
				
			||||||
 | 
							if err := testutil.GatherAndCompare(legacyregistry.DefaultGatherer, strings.NewReader(expectedValue), metrics...); err != nil {
 | 
				
			||||||
 | 
								t.Fatal(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							authorizationDecisionsTotal.Reset()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// unknown decision emits a metric
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							dummyAuthorizer.decision = authorizer.DecisionDeny + 10
 | 
				
			||||||
 | 
							_, _, _ = a.Authorize(context.Background(), nil)
 | 
				
			||||||
 | 
							expectedValue := prefix + `
 | 
				
			||||||
 | 
								apiserver_authorization_decisions_total{decision="unknown",name="myname",type="mytype"} 1
 | 
				
			||||||
 | 
							`
 | 
				
			||||||
 | 
							if err := testutil.GatherAndCompare(legacyregistry.DefaultGatherer, strings.NewReader(expectedValue), metrics...); err != nil {
 | 
				
			||||||
 | 
								t.Fatal(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							authorizationDecisionsTotal.Reset()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type dummyAuthorizer struct {
 | 
				
			||||||
 | 
						decision authorizer.Decision
 | 
				
			||||||
 | 
						err      error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *dummyAuthorizer) Authorize(ctx context.Context, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
 | 
				
			||||||
 | 
						return d.decision, "", d.err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -73,7 +73,6 @@ func RegisterMetrics() {
 | 
				
			|||||||
func ResetMetricsForTest() {
 | 
					func ResetMetricsForTest() {
 | 
				
			||||||
	authorizationConfigAutomaticReloadsTotal.Reset()
 | 
						authorizationConfigAutomaticReloadsTotal.Reset()
 | 
				
			||||||
	authorizationConfigAutomaticReloadLastTimestampSeconds.Reset()
 | 
						authorizationConfigAutomaticReloadLastTimestampSeconds.Reset()
 | 
				
			||||||
	legacyregistry.Reset()
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func RecordAuthorizationConfigAutomaticReloadFailure(apiServerID string) {
 | 
					func RecordAuthorizationConfigAutomaticReloadFailure(apiServerID string) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,6 +25,7 @@ import (
 | 
				
			|||||||
	"net/http/httptest"
 | 
						"net/http/httptest"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync/atomic"
 | 
						"sync/atomic"
 | 
				
			||||||
@@ -35,6 +36,7 @@ import (
 | 
				
			|||||||
	rbacv1 "k8s.io/api/rbac/v1"
 | 
						rbacv1 "k8s.io/api/rbac/v1"
 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/util/wait"
 | 
						"k8s.io/apimachinery/pkg/util/wait"
 | 
				
			||||||
 | 
						authorizationmetrics "k8s.io/apiserver/pkg/authorization/metrics"
 | 
				
			||||||
	"k8s.io/apiserver/pkg/features"
 | 
						"k8s.io/apiserver/pkg/features"
 | 
				
			||||||
	authzmetrics "k8s.io/apiserver/pkg/server/options/authorizationconfig/metrics"
 | 
						authzmetrics "k8s.io/apiserver/pkg/server/options/authorizationconfig/metrics"
 | 
				
			||||||
	utilfeature "k8s.io/apiserver/pkg/util/feature"
 | 
						utilfeature "k8s.io/apiserver/pkg/util/feature"
 | 
				
			||||||
@@ -178,6 +180,7 @@ users:
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// returns a deny response when called
 | 
						// returns a deny response when called
 | 
				
			||||||
 | 
						denyName := "deny.example.com"
 | 
				
			||||||
	serverDenyCalled := atomic.Int32{}
 | 
						serverDenyCalled := atomic.Int32{}
 | 
				
			||||||
	serverDeny := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
						serverDeny := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
		serverDenyCalled.Add(1)
 | 
							serverDenyCalled.Add(1)
 | 
				
			||||||
@@ -221,6 +224,7 @@ users:
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// returns an allow response when called
 | 
						// returns an allow response when called
 | 
				
			||||||
 | 
						allowName := "allow.example.com"
 | 
				
			||||||
	serverAllowCalled := atomic.Int32{}
 | 
						serverAllowCalled := atomic.Int32{}
 | 
				
			||||||
	serverAllow := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
						serverAllow := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
		serverAllowCalled.Add(1)
 | 
							serverAllowCalled.Add(1)
 | 
				
			||||||
@@ -242,6 +246,7 @@ users:
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// returns an allow response when called
 | 
						// returns an allow response when called
 | 
				
			||||||
 | 
						allowReloadedName := "allowreloaded.example.com"
 | 
				
			||||||
	serverAllowReloadedCalled := atomic.Int32{}
 | 
						serverAllowReloadedCalled := atomic.Int32{}
 | 
				
			||||||
	serverAllowReloaded := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
						serverAllowReloaded := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
		serverAllowReloadedCalled.Add(1)
 | 
							serverAllowReloadedCalled.Add(1)
 | 
				
			||||||
@@ -269,9 +274,15 @@ users:
 | 
				
			|||||||
		serverNoOpinionCalled.Store(0)
 | 
							serverNoOpinionCalled.Store(0)
 | 
				
			||||||
		serverAllowCalled.Store(0)
 | 
							serverAllowCalled.Store(0)
 | 
				
			||||||
		serverAllowReloadedCalled.Store(0)
 | 
							serverAllowReloadedCalled.Store(0)
 | 
				
			||||||
 | 
							authorizationmetrics.ResetMetricsForTest()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						var adminClient *clientset.Clientset
 | 
				
			||||||
	assertCounts := func(errorCount, timeoutCount, denyCount, noOpinionCount, allowCount, allowReloadedCount int32) {
 | 
						assertCounts := func(errorCount, timeoutCount, denyCount, noOpinionCount, allowCount, allowReloadedCount int32) {
 | 
				
			||||||
		t.Helper()
 | 
							t.Helper()
 | 
				
			||||||
 | 
							metrics, err := getMetrics(t, adminClient)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("error getting metrics: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if e, a := errorCount, serverErrorCalled.Load(); e != a {
 | 
							if e, a := errorCount, serverErrorCalled.Load(); e != a {
 | 
				
			||||||
			t.Errorf("expected fail webhook calls: %d, got %d", e, a)
 | 
								t.Errorf("expected fail webhook calls: %d, got %d", e, a)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -281,15 +292,24 @@ users:
 | 
				
			|||||||
		if e, a := denyCount, serverDenyCalled.Load(); e != a {
 | 
							if e, a := denyCount, serverDenyCalled.Load(); e != a {
 | 
				
			||||||
			t.Errorf("expected deny webhook calls: %d, got %d", e, a)
 | 
								t.Errorf("expected deny webhook calls: %d, got %d", e, a)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if e, a := denyCount, metrics.decisions[authorizerKey{authorizerType: "Webhook", authorizerName: denyName}]["denied"]; e != int32(a) {
 | 
				
			||||||
 | 
								t.Errorf("expected deny webhook denied metrics calls: %d, got %d", e, a)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if e, a := noOpinionCount, serverNoOpinionCalled.Load(); e != a {
 | 
							if e, a := noOpinionCount, serverNoOpinionCalled.Load(); e != a {
 | 
				
			||||||
			t.Errorf("expected noOpinion webhook calls: %d, got %d", e, a)
 | 
								t.Errorf("expected noOpinion webhook calls: %d, got %d", e, a)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if e, a := allowCount, serverAllowCalled.Load(); e != a {
 | 
							if e, a := allowCount, serverAllowCalled.Load(); e != a {
 | 
				
			||||||
			t.Errorf("expected allow webhook calls: %d, got %d", e, a)
 | 
								t.Errorf("expected allow webhook calls: %d, got %d", e, a)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if e, a := allowCount, metrics.decisions[authorizerKey{authorizerType: "Webhook", authorizerName: allowName}]["allowed"]; e != int32(a) {
 | 
				
			||||||
 | 
								t.Errorf("expected allow webhook allowed metrics calls: %d, got %d", e, a)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if e, a := allowReloadedCount, serverAllowReloadedCalled.Load(); e != a {
 | 
							if e, a := allowReloadedCount, serverAllowReloadedCalled.Load(); e != a {
 | 
				
			||||||
			t.Errorf("expected allowReloaded webhook calls: %d, got %d", e, a)
 | 
								t.Errorf("expected allowReloaded webhook calls: %d, got %d", e, a)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if e, a := allowReloadedCount, metrics.decisions[authorizerKey{authorizerType: "Webhook", authorizerName: allowReloadedName}]["allowed"]; e != int32(a) {
 | 
				
			||||||
 | 
								t.Errorf("expected allowReloaded webhook allowed metrics calls: %d, got %d", e, a)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		resetCounts()
 | 
							resetCounts()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -333,7 +353,7 @@ authorizers:
 | 
				
			|||||||
    - expression: 'request.resourceAttributes.name == "timeout"'
 | 
					    - expression: 'request.resourceAttributes.name == "timeout"'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- type: Webhook
 | 
					- type: Webhook
 | 
				
			||||||
  name: deny.example.com
 | 
					  name: `+denyName+`
 | 
				
			||||||
  webhook:
 | 
					  webhook:
 | 
				
			||||||
    timeout: 5s
 | 
					    timeout: 5s
 | 
				
			||||||
    failurePolicy: NoOpinion
 | 
					    failurePolicy: NoOpinion
 | 
				
			||||||
@@ -361,7 +381,7 @@ authorizers:
 | 
				
			|||||||
      kubeConfigFile: `+serverNoOpinionKubeconfigName+`
 | 
					      kubeConfigFile: `+serverNoOpinionKubeconfigName+`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- type: Webhook
 | 
					- type: Webhook
 | 
				
			||||||
  name: allow.example.com
 | 
					  name: `+allowName+`
 | 
				
			||||||
  webhook:
 | 
					  webhook:
 | 
				
			||||||
    timeout: 5s
 | 
					    timeout: 5s
 | 
				
			||||||
    failurePolicy: Deny
 | 
					    failurePolicy: Deny
 | 
				
			||||||
@@ -383,7 +403,7 @@ authorizers:
 | 
				
			|||||||
	)
 | 
						)
 | 
				
			||||||
	t.Cleanup(server.TearDownFn)
 | 
						t.Cleanup(server.TearDownFn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	adminClient := clientset.NewForConfigOrDie(server.ClientConfig)
 | 
						adminClient = clientset.NewForConfigOrDie(server.ClientConfig)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// malformed webhook short circuits
 | 
						// malformed webhook short circuits
 | 
				
			||||||
	t.Log("checking error")
 | 
						t.Log("checking error")
 | 
				
			||||||
@@ -470,14 +490,14 @@ authorizers:
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// check last loaded success/failure metric timestamps, ensure success is present, failure is not
 | 
						// check last loaded success/failure metric timestamps, ensure success is present, failure is not
 | 
				
			||||||
	initialReloadSuccess, initialReloadFailure, err := getReloadTimes(t, adminClient)
 | 
						initialMetrics, err := getMetrics(t, adminClient)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if initialReloadSuccess == nil {
 | 
						if initialMetrics.reloadSuccess == nil {
 | 
				
			||||||
		t.Fatal("expected success timestamp, got none")
 | 
							t.Fatal("expected success timestamp, got none")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if initialReloadFailure != nil {
 | 
						if initialMetrics.reloadFailure != nil {
 | 
				
			||||||
		t.Fatal("expected no failure timestamp, got one")
 | 
							t.Fatal("expected no failure timestamp, got one")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -487,24 +507,24 @@ authorizers:
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// wait for failure timestamp > success timestamp
 | 
						// wait for failure timestamp > success timestamp
 | 
				
			||||||
	var reload1Success, reload1Failure *time.Time
 | 
						var reload1Metrics *metrics
 | 
				
			||||||
	err = wait.PollUntilContextTimeout(context.TODO(), time.Second, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) {
 | 
						err = wait.PollUntilContextTimeout(context.TODO(), time.Second, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) {
 | 
				
			||||||
		reload1Success, reload1Failure, err = getReloadTimes(t, adminClient)
 | 
							reload1Metrics, err = getMetrics(t, adminClient)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Fatal(err)
 | 
								t.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if reload1Success == nil {
 | 
							if reload1Metrics.reloadSuccess == nil {
 | 
				
			||||||
			t.Fatal("expected success timestamp, got none")
 | 
								t.Fatal("expected success timestamp, got none")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if !reload1Success.Equal(*initialReloadSuccess) {
 | 
							if !reload1Metrics.reloadSuccess.Equal(*initialMetrics.reloadSuccess) {
 | 
				
			||||||
			t.Fatalf("success timestamp changed from initial success %s to %s unexpectedly", initialReloadSuccess.String(), reload1Success.String())
 | 
								t.Fatalf("success timestamp changed from initial success %s to %s unexpectedly", initialMetrics.reloadSuccess.String(), reload1Metrics.reloadSuccess.String())
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if reload1Failure == nil {
 | 
							if reload1Metrics.reloadFailure == nil {
 | 
				
			||||||
			t.Log("expected failure timestamp, got nil, retrying")
 | 
								t.Log("expected failure timestamp, got nil, retrying")
 | 
				
			||||||
			return false, nil
 | 
								return false, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if !reload1Failure.After(*reload1Success) {
 | 
							if !reload1Metrics.reloadFailure.After(*reload1Metrics.reloadSuccess) {
 | 
				
			||||||
			t.Fatalf("expected failure timestamp to be more recent than success timestamp, got %s <= %s", reload1Failure.String(), reload1Success.String())
 | 
								t.Fatalf("expected failure timestamp to be more recent than success timestamp, got %s <= %s", reload1Metrics.reloadFailure.String(), reload1Metrics.reloadSuccess.String())
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return true, nil
 | 
							return true, nil
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
@@ -539,7 +559,7 @@ apiVersion: apiserver.config.k8s.io/v1alpha1
 | 
				
			|||||||
kind: AuthorizationConfiguration
 | 
					kind: AuthorizationConfiguration
 | 
				
			||||||
authorizers:
 | 
					authorizers:
 | 
				
			||||||
- type: Webhook
 | 
					- type: Webhook
 | 
				
			||||||
  name: allowreloaded.example.com
 | 
					  name: `+allowReloadedName+`
 | 
				
			||||||
  webhook:
 | 
					  webhook:
 | 
				
			||||||
    timeout: 5s
 | 
					    timeout: 5s
 | 
				
			||||||
    failurePolicy: Deny
 | 
					    failurePolicy: Deny
 | 
				
			||||||
@@ -553,29 +573,29 @@ authorizers:
 | 
				
			|||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// wait for success timestamp > reload1Failure timestamp
 | 
						// wait for success timestamp > reload1Metrics.reloadFailure timestamp
 | 
				
			||||||
	var reload2Success, reload2Failure *time.Time
 | 
						var reload2Metrics *metrics
 | 
				
			||||||
	err = wait.PollUntilContextTimeout(context.TODO(), time.Second, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) {
 | 
						err = wait.PollUntilContextTimeout(context.TODO(), time.Second, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) {
 | 
				
			||||||
		reload2Success, reload2Failure, err = getReloadTimes(t, adminClient)
 | 
							reload2Metrics, err = getMetrics(t, adminClient)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Fatal(err)
 | 
								t.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if reload2Failure == nil {
 | 
							if reload2Metrics.reloadFailure == nil {
 | 
				
			||||||
			t.Log("expected failure timestamp, got nil, retrying")
 | 
								t.Log("expected failure timestamp, got nil, retrying")
 | 
				
			||||||
			return false, nil
 | 
								return false, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if !reload2Failure.Equal(*reload1Failure) {
 | 
							if !reload2Metrics.reloadFailure.Equal(*reload1Metrics.reloadFailure) {
 | 
				
			||||||
			t.Fatalf("failure timestamp changed from reload1Failure %s to %s unexpectedly", reload1Failure.String(), reload2Failure.String())
 | 
								t.Fatalf("failure timestamp changed from reload1Metrics.reloadFailure %s to %s unexpectedly", reload1Metrics.reloadFailure.String(), reload2Metrics.reloadFailure.String())
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if reload2Success == nil {
 | 
							if reload2Metrics.reloadSuccess == nil {
 | 
				
			||||||
			t.Fatal("expected success timestamp, got none")
 | 
								t.Fatal("expected success timestamp, got none")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if reload2Success.Equal(*initialReloadSuccess) {
 | 
							if reload2Metrics.reloadSuccess.Equal(*initialMetrics.reloadSuccess) {
 | 
				
			||||||
			t.Log("success timestamp hasn't updated from initial success, retrying")
 | 
								t.Log("success timestamp hasn't updated from initial success, retrying")
 | 
				
			||||||
			return false, nil
 | 
								return false, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if !reload2Success.After(*reload2Failure) {
 | 
							if !reload2Metrics.reloadSuccess.After(*reload2Metrics.reloadFailure) {
 | 
				
			||||||
			t.Fatalf("expected success timestamp to be more recent than failure, got %s <= %s", reload2Success.String(), reload2Failure.String())
 | 
								t.Fatalf("expected success timestamp to be more recent than failure, got %s <= %s", reload2Metrics.reloadSuccess.String(), reload2Metrics.reloadFailure.String())
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return true, nil
 | 
							return true, nil
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
@@ -610,28 +630,28 @@ authorizers:
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// wait for failure timestamp > success timestamp
 | 
						// wait for failure timestamp > success timestamp
 | 
				
			||||||
	var reload3Success, reload3Failure *time.Time
 | 
						var reload3Metrics *metrics
 | 
				
			||||||
	err = wait.PollUntilContextTimeout(context.TODO(), time.Second, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) {
 | 
						err = wait.PollUntilContextTimeout(context.TODO(), time.Second, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) {
 | 
				
			||||||
		reload3Success, reload3Failure, err = getReloadTimes(t, adminClient)
 | 
							reload3Metrics, err = getMetrics(t, adminClient)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Fatal(err)
 | 
								t.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if reload3Success == nil {
 | 
							if reload3Metrics.reloadSuccess == nil {
 | 
				
			||||||
			t.Fatal("expected success timestamp, got none")
 | 
								t.Fatal("expected success timestamp, got none")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if !reload3Success.Equal(*reload2Success) {
 | 
							if !reload3Metrics.reloadSuccess.Equal(*reload2Metrics.reloadSuccess) {
 | 
				
			||||||
			t.Fatalf("success timestamp changed from %s to %s unexpectedly", reload2Success.String(), reload3Success.String())
 | 
								t.Fatalf("success timestamp changed from %s to %s unexpectedly", reload2Metrics.reloadSuccess.String(), reload3Metrics.reloadSuccess.String())
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if reload3Failure == nil {
 | 
							if reload3Metrics.reloadFailure == nil {
 | 
				
			||||||
			t.Log("expected failure timestamp, got nil, retrying")
 | 
								t.Log("expected failure timestamp, got nil, retrying")
 | 
				
			||||||
			return false, nil
 | 
								return false, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if reload3Failure.Equal(*reload2Failure) {
 | 
							if reload3Metrics.reloadFailure.Equal(*reload2Metrics.reloadFailure) {
 | 
				
			||||||
			t.Log("failure timestamp hasn't updated, retrying")
 | 
								t.Log("failure timestamp hasn't updated, retrying")
 | 
				
			||||||
			return false, nil
 | 
								return false, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if !reload3Failure.After(*reload3Success) {
 | 
							if !reload3Metrics.reloadFailure.After(*reload3Metrics.reloadSuccess) {
 | 
				
			||||||
			t.Fatalf("expected failure timestamp to be more recent than success, got %s <= %s", reload3Failure.String(), reload3Success.String())
 | 
								t.Fatalf("expected failure timestamp to be more recent than success, got %s <= %s", reload3Metrics.reloadFailure.String(), reload3Metrics.reloadSuccess.String())
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return true, nil
 | 
							return true, nil
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
@@ -661,36 +681,69 @@ authorizers:
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getReloadTimes(t *testing.T, client *clientset.Clientset) (*time.Time, *time.Time, error) {
 | 
					type metrics struct {
 | 
				
			||||||
 | 
						reloadSuccess *time.Time
 | 
				
			||||||
 | 
						reloadFailure *time.Time
 | 
				
			||||||
 | 
						decisions     map[authorizerKey]map[string]int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					type authorizerKey struct {
 | 
				
			||||||
 | 
						authorizerType string
 | 
				
			||||||
 | 
						authorizerName string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var decisionMetric = regexp.MustCompile(`apiserver_authorization_decisions_total\{decision="(.*?)",name="(.*?)",type="(.*?)"\} (\d+)`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getMetrics(t *testing.T, client *clientset.Clientset) (*metrics, error) {
 | 
				
			||||||
	data, err := client.RESTClient().Get().AbsPath("/metrics").DoRaw(context.TODO())
 | 
						data, err := client.RESTClient().Get().AbsPath("/metrics").DoRaw(context.TODO())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//  apiserver_authorization_config_controller_automatic_reload_last_timestamp_seconds{apiserver_id_hash="sha256:4b86cfa719a83dd63a4dc6a9831edb2b59240d0f59cf215b2d51aacb3f5c395e",status="success"} 1.7002567356895502e+09
 | 
						//  apiserver_authorization_config_controller_automatic_reload_last_timestamp_seconds{apiserver_id_hash="sha256:4b86cfa719a83dd63a4dc6a9831edb2b59240d0f59cf215b2d51aacb3f5c395e",status="success"} 1.7002567356895502e+09
 | 
				
			||||||
	//  apiserver_authorization_config_controller_automatic_reload_last_timestamp_seconds{apiserver_id_hash="sha256:4b86cfa719a83dd63a4dc6a9831edb2b59240d0f59cf215b2d51aacb3f5c395e",status="failure"} 1.7002567356895502e+09
 | 
						//  apiserver_authorization_config_controller_automatic_reload_last_timestamp_seconds{apiserver_id_hash="sha256:4b86cfa719a83dd63a4dc6a9831edb2b59240d0f59cf215b2d51aacb3f5c395e",status="failure"} 1.7002567356895502e+09
 | 
				
			||||||
 | 
						//  apiserver_authorization_decisions_total{decision="allowed",name="allow.example.com",type="Webhook"} 2
 | 
				
			||||||
 | 
						//  apiserver_authorization_decisions_total{decision="allowed",name="allowreloaded.example.com",type="Webhook"} 1
 | 
				
			||||||
 | 
						//  apiserver_authorization_decisions_total{decision="denied",name="deny.example.com",type="Webhook"} 1
 | 
				
			||||||
 | 
						//  apiserver_authorization_decisions_total{decision="denied",name="error.example.com",type="Webhook"} 1
 | 
				
			||||||
 | 
						//  apiserver_authorization_decisions_total{decision="denied",name="timeout.example.com",type="Webhook"} 1
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var success, failure *time.Time
 | 
						var m metrics
 | 
				
			||||||
	for _, line := range strings.Split(string(data), "\n") {
 | 
						for _, line := range strings.Split(string(data), "\n") {
 | 
				
			||||||
 | 
							if matches := decisionMetric.FindStringSubmatch(line); matches != nil {
 | 
				
			||||||
 | 
								t.Log(line)
 | 
				
			||||||
 | 
								if m.decisions == nil {
 | 
				
			||||||
 | 
									m.decisions = map[authorizerKey]map[string]int{}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								key := authorizerKey{authorizerType: matches[3], authorizerName: matches[2]}
 | 
				
			||||||
 | 
								if m.decisions[key] == nil {
 | 
				
			||||||
 | 
									m.decisions[key] = map[string]int{}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								count, err := strconv.Atoi(matches[4])
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								m.decisions[key][matches[1]] = count
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if strings.HasPrefix(line, "apiserver_authorization_config_controller_automatic_reload_last_timestamp_seconds") {
 | 
							if strings.HasPrefix(line, "apiserver_authorization_config_controller_automatic_reload_last_timestamp_seconds") {
 | 
				
			||||||
			t.Log(line)
 | 
								t.Log(line)
 | 
				
			||||||
			values := strings.Split(line, " ")
 | 
								values := strings.Split(line, " ")
 | 
				
			||||||
			value, err := strconv.ParseFloat(values[len(values)-1], 64)
 | 
								value, err := strconv.ParseFloat(values[len(values)-1], 64)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return nil, nil, err
 | 
									return nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			seconds := int64(value)
 | 
								seconds := int64(value)
 | 
				
			||||||
			nanoseconds := int64((value - float64(seconds)) * 1000000000)
 | 
								nanoseconds := int64((value - float64(seconds)) * 1000000000)
 | 
				
			||||||
			tm := time.Unix(seconds, nanoseconds)
 | 
								tm := time.Unix(seconds, nanoseconds)
 | 
				
			||||||
			if strings.Contains(line, `"success"`) {
 | 
								if strings.Contains(line, `"success"`) {
 | 
				
			||||||
				success = &tm
 | 
									m.reloadSuccess = &tm
 | 
				
			||||||
				t.Log("success", success.String())
 | 
									t.Log("success", m.reloadSuccess.String())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if strings.Contains(line, `"failure"`) {
 | 
								if strings.Contains(line, `"failure"`) {
 | 
				
			||||||
				failure = &tm
 | 
									m.reloadFailure = &tm
 | 
				
			||||||
				t.Log("failure", failure.String())
 | 
									t.Log("failure", m.reloadFailure.String())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return success, failure, nil
 | 
						return &m, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							@@ -1428,6 +1428,7 @@ k8s.io/apiserver/pkg/authentication/user
 | 
				
			|||||||
k8s.io/apiserver/pkg/authorization/authorizer
 | 
					k8s.io/apiserver/pkg/authorization/authorizer
 | 
				
			||||||
k8s.io/apiserver/pkg/authorization/authorizerfactory
 | 
					k8s.io/apiserver/pkg/authorization/authorizerfactory
 | 
				
			||||||
k8s.io/apiserver/pkg/authorization/cel
 | 
					k8s.io/apiserver/pkg/authorization/cel
 | 
				
			||||||
 | 
					k8s.io/apiserver/pkg/authorization/metrics
 | 
				
			||||||
k8s.io/apiserver/pkg/authorization/path
 | 
					k8s.io/apiserver/pkg/authorization/path
 | 
				
			||||||
k8s.io/apiserver/pkg/authorization/union
 | 
					k8s.io/apiserver/pkg/authorization/union
 | 
				
			||||||
k8s.io/apiserver/pkg/cel
 | 
					k8s.io/apiserver/pkg/cel
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user