1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 07:57:07 +00:00

feat: add reporting to validating admission handler (#12090)

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2025-02-04 21:32:18 +01:00 committed by GitHub
parent 9e8b655f6f
commit 4a4aef54d3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 69 additions and 11 deletions

View file

@ -94,7 +94,7 @@ func initMetricsFlags() {
} }
func initKubeconfigFlags(qps float64, burst int, eventsQPS float64, eventsBurst int) { func initKubeconfigFlags(qps float64, burst int, eventsQPS float64, eventsBurst int) {
if f := flag.CommandLine.Lookup("kubeconfig"); f == nil { if f := flag.Lookup("kubeconfig"); f == nil {
flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
} }
flag.Float64Var(&clientRateLimitQPS, "clientRateLimitQPS", qps, "Configure the maximum QPS to the Kubernetes API server from Kyverno. Uses the client default if zero.") flag.Float64Var(&clientRateLimitQPS, "clientRateLimitQPS", qps, "Configure the maximum QPS to the Kubernetes API server from Kyverno. Uses the client default if zero.")
@ -149,8 +149,8 @@ func initReportingFlags() {
} }
func lookupKubeconfigFlag() { func lookupKubeconfigFlag() {
if f := flag.CommandLine.Lookup("kubeconfig"); f != nil { if f := flag.Lookup("kubeconfig"); f != nil {
kubeconfig = flag.CommandLine.Lookup("kubeconfig").Value.String() kubeconfig = f.Value.String()
} }
} }

View file

@ -605,7 +605,6 @@ func main() {
matching.NewMatcher(), matching.NewMatcher(),
) )
} }
voplHandlers := vpol.New(celEngine, contextProvider)
ephrs, err := breaker.StartAdmissionReportsCounter(signalCtx, setup.MetadataClient) ephrs, err := breaker.StartAdmissionReportsCounter(signalCtx, setup.MetadataClient)
if err != nil { if err != nil {
setup.Logger.Error(err, "failed to start admission reports watcher") setup.Logger.Error(err, "failed to start admission reports watcher")
@ -640,6 +639,12 @@ func main() {
setup.ReportingConfiguration, setup.ReportingConfiguration,
reportsBreaker, reportsBreaker,
) )
voplHandlers := vpol.New(
celEngine,
contextProvider,
setup.KyvernoClient,
reportsBreaker,
)
exceptionHandlers := webhooksexception.NewHandlers(exception.ValidationOptions{ exceptionHandlers := webhooksexception.NewHandlers(exception.ValidationOptions{
Enabled: internal.PolicyExceptionEnabled(), Enabled: internal.PolicyExceptionEnabled(),
Namespace: internal.ExceptionNamespace(), Namespace: internal.ExceptionNamespace(),

View file

@ -6,24 +6,37 @@ import (
"time" "time"
"github.com/go-logr/logr" "github.com/go-logr/logr"
"github.com/kyverno/kyverno/pkg/breaker"
celengine "github.com/kyverno/kyverno/pkg/cel/engine" celengine "github.com/kyverno/kyverno/pkg/cel/engine"
celpolicy "github.com/kyverno/kyverno/pkg/cel/policy" celpolicy "github.com/kyverno/kyverno/pkg/cel/policy"
"github.com/kyverno/kyverno/pkg/client/clientset/versioned"
engineapi "github.com/kyverno/kyverno/pkg/engine/api" engineapi "github.com/kyverno/kyverno/pkg/engine/api"
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
reportutils "github.com/kyverno/kyverno/pkg/utils/report"
"github.com/kyverno/kyverno/pkg/webhooks/handlers" "github.com/kyverno/kyverno/pkg/webhooks/handlers"
"go.uber.org/multierr" "go.uber.org/multierr"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1" admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
"k8s.io/apimachinery/pkg/util/wait"
) )
type handler struct { type handler struct {
context celpolicy.Context context celpolicy.Context
engine celengine.Engine engine celengine.Engine
kyvernoClient versioned.Interface
reportsBreaker breaker.Breaker
} }
func New(engine celengine.Engine, context celpolicy.Context) *handler { func New(
engine celengine.Engine,
context celpolicy.Context,
kyvernoClient versioned.Interface,
reportsBreaker breaker.Breaker,
) *handler {
return &handler{ return &handler{
context: context, context: context,
engine: engine, engine: engine,
kyvernoClient: kyvernoClient,
reportsBreaker: reportsBreaker,
} }
} }
@ -35,10 +48,18 @@ func (h *handler) Validate(ctx context.Context, logger logr.Logger, request hand
if err != nil { if err != nil {
return admissionutils.Response(request.UID, err) return admissionutils.Response(request.UID, err)
} }
return admissionResponse(response, request) var group wait.Group
defer group.Wait()
group.Start(func() {
err := h.admissionReport(ctx, response, request)
if err != nil {
logger.Error(err, "failed to create report")
}
})
return h.admissionResponse(response, request)
} }
func admissionResponse(response celengine.EngineResponse, request handlers.AdmissionRequest) handlers.AdmissionResponse { func (h *handler) admissionResponse(response celengine.EngineResponse, request handlers.AdmissionRequest) handlers.AdmissionResponse {
var errs []error var errs []error
var warnings []string var warnings []string
for _, policy := range response.Policies { for _, policy := range response.Policies {
@ -65,3 +86,35 @@ func admissionResponse(response celengine.EngineResponse, request handlers.Admis
} }
return admissionutils.Response(request.UID, multierr.Combine(errs...), warnings...) return admissionutils.Response(request.UID, multierr.Combine(errs...), warnings...)
} }
func (h *handler) admissionReport(ctx context.Context, response celengine.EngineResponse, request handlers.AdmissionRequest) error {
object, oldObject, err := admissionutils.ExtractResources(nil, request.AdmissionRequest)
if err != nil {
return err
}
if object.Object == nil {
object = oldObject
}
responses := make([]engineapi.EngineResponse, 0, len(response.Policies))
for _, r := range response.Policies {
engineResponse := engineapi.EngineResponse{
Resource: object,
PolicyResponse: engineapi.PolicyResponse{
Rules: r.Rules,
},
}
engineResponse = engineResponse.WithPolicy(engineapi.NewValidatingPolicy(&r.Policy))
responses = append(responses, engineResponse)
}
report := reportutils.BuildAdmissionReport(object, request.AdmissionRequest, responses...)
if len(report.GetResults()) > 0 {
err := h.reportsBreaker.Do(ctx, func(ctx context.Context) error {
_, err := reportutils.CreateReport(ctx, report, h.kyvernoClient)
return err
})
if err != nil {
return err
}
}
return nil
}