2020-01-07 10:33:28 -08:00
package webhooks
import (
2020-11-09 11:26:12 -08:00
contextdefault "context"
2021-04-27 01:28:34 +05:30
"encoding/json"
2020-07-09 11:48:34 -07:00
"fmt"
2020-08-31 23:55:13 +05:30
"reflect"
"sort"
"strings"
"time"
2020-10-07 11:12:31 -07:00
2020-12-22 11:07:31 -08:00
"github.com/go-logr/logr"
2020-10-07 11:12:31 -07:00
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
v1 "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
2021-02-04 02:39:42 +05:30
"github.com/kyverno/kyverno/pkg/common"
2020-10-07 11:12:31 -07:00
"github.com/kyverno/kyverno/pkg/config"
2021-04-27 01:28:34 +05:30
client "github.com/kyverno/kyverno/pkg/dclient"
2020-10-07 11:12:31 -07:00
"github.com/kyverno/kyverno/pkg/engine"
"github.com/kyverno/kyverno/pkg/engine/context"
2021-04-27 01:28:34 +05:30
enginectx "github.com/kyverno/kyverno/pkg/engine/context"
2020-10-07 11:12:31 -07:00
"github.com/kyverno/kyverno/pkg/engine/response"
2020-12-23 23:44:17 +05:30
enginutils "github.com/kyverno/kyverno/pkg/engine/utils"
2021-04-27 01:28:34 +05:30
"github.com/kyverno/kyverno/pkg/engine/variables"
2020-10-07 11:12:31 -07:00
"github.com/kyverno/kyverno/pkg/event"
2021-03-26 05:34:43 +05:30
gen "github.com/kyverno/kyverno/pkg/generate"
2021-05-15 19:15:04 +05:30
"github.com/kyverno/kyverno/pkg/metrics"
2021-05-15 21:34:26 +05:30
policyRuleExecutionLatency "github.com/kyverno/kyverno/pkg/metrics/policyruleexecutionlatency"
2021-05-15 19:15:04 +05:30
policyRuleResults "github.com/kyverno/kyverno/pkg/metrics/policyruleresults"
2020-12-01 12:30:08 -08:00
kyvernoutils "github.com/kyverno/kyverno/pkg/utils"
2020-10-07 11:12:31 -07:00
"github.com/kyverno/kyverno/pkg/webhooks/generate"
v1beta1 "k8s.io/api/admission/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2020-12-15 04:22:13 +05:30
"k8s.io/apimachinery/pkg/labels"
2020-01-07 10:33:28 -08:00
)
2020-01-24 12:05:53 -08:00
//HandleGenerate handles admission-requests for policies with generate rules
2021-05-15 23:33:41 +05:30
func ( ws * WebhookServer ) HandleGenerate ( request * v1beta1 . AdmissionRequest , policies [ ] * kyverno . ClusterPolicy , ctx * context . Context , userRequestInfo kyverno . RequestInfo , dynamicConfig config . Interface , admissionRequestTimestamp int64 , latencySender * chan int64 ) {
2021-05-13 12:03:13 -07:00
logger := ws . log . WithValues ( "action" , "generation" , "uid" , request . UID , "kind" , request . Kind , "namespace" , request . Namespace , "name" , request . Name , "operation" , request . Operation , "gvk" , request . Kind . String ( ) )
2020-03-17 11:05:20 -07:00
logger . V ( 4 ) . Info ( "incoming request" )
2020-12-23 15:10:07 -08:00
var engineResponses [ ] * response . EngineResponse
2020-12-30 00:12:36 +05:30
if request . Operation == v1beta1 . Create || request . Operation == v1beta1 . Update {
if len ( policies ) == 0 {
return
}
// convert RAW to unstructured
new , old , err := kyvernoutils . ExtractResources ( nil , request )
if err != nil {
logger . Error ( err , "failed to extract resource" )
}
2020-01-07 10:33:28 -08:00
2021-02-01 12:59:13 -08:00
policyContext := & engine . PolicyContext {
2020-12-30 00:12:36 +05:30
NewResource : new ,
OldResource : old ,
AdmissionInfo : userRequestInfo ,
ExcludeGroupRole : dynamicConfig . GetExcludeGroupRole ( ) ,
ExcludeResourceFunc : ws . configHandler . ToFilter ,
ResourceCache : ws . resCache ,
JSONContext : ctx ,
2021-02-22 12:08:26 -08:00
Client : ws . client ,
2020-12-30 00:12:36 +05:30
}
2020-01-07 10:33:28 -08:00
2020-12-30 00:12:36 +05:30
for _ , policy := range policies {
var rules [ ] response . RuleResponse
policyContext . Policy = * policy
2021-02-04 02:39:42 +05:30
if request . Kind . Kind != "Namespace" && request . Namespace != "" {
policyContext . NamespaceLabels = common . GetNamespaceSelectorsFromNamespaceLister ( request . Kind . Kind , request . Namespace , ws . nsLister , logger )
}
2020-12-30 00:12:36 +05:30
engineResponse := engine . Generate ( policyContext )
for _ , rule := range engineResponse . PolicyResponse . Rules {
if ! rule . Success {
ws . deleteGR ( logger , engineResponse )
continue
}
rules = append ( rules , rule )
}
2020-01-07 10:33:28 -08:00
2020-12-30 00:12:36 +05:30
if len ( rules ) > 0 {
engineResponse . PolicyResponse . Rules = rules
// some generate rules do apply to the resource
engineResponses = append ( engineResponses , engineResponse )
ws . statusListener . Update ( generateStats {
resp : engineResponse ,
} )
2020-08-31 23:55:13 +05:30
}
2021-05-15 19:15:04 +05:30
// registering the kyverno_policy_rule_results_info metric concurrently
go ws . registerPolicyRuleResultsMetricGeneration ( logger , string ( request . Operation ) , * policy , * engineResponse , admissionRequestTimestamp )
2021-05-15 21:34:26 +05:30
// registering the kyverno_policy_rule_execution_latency_milliseconds metric concurrently
go ws . registerPolicyRuleExecutionLatencyMetricGenerate ( logger , string ( request . Operation ) , * policy , * engineResponse , admissionRequestTimestamp )
2020-08-31 23:55:13 +05:30
}
2020-09-04 03:04:23 +05:30
2020-12-30 00:12:36 +05:30
// Adds Generate Request to a channel(queue size 1000) to generators
if failedResponse := applyGenerateRequest ( ws . grGenerator , userRequestInfo , request . Operation , engineResponses ... ) ; err != nil {
// report failure event
for _ , failedGR := range failedResponse {
events := failedEvents ( fmt . Errorf ( "failed to create Generate Request: %v" , failedGR . err ) , failedGR . gr , new )
ws . eventGen . Add ( events ... )
}
2020-01-07 10:33:28 -08:00
}
2020-08-31 23:55:13 +05:30
}
2020-09-04 03:04:23 +05:30
2020-12-30 00:12:36 +05:30
if request . Operation == v1beta1 . Update {
2021-01-27 23:41:22 +05:30
ws . handleUpdate ( request , policies )
2020-01-07 10:33:28 -08:00
}
2021-05-15 23:33:41 +05:30
// sending the admission request latency to other goroutine (reporting the metrics) over the channel
admissionReviewLatencyDuration := int64 ( time . Since ( time . Unix ( admissionRequestTimestamp , 0 ) ) )
* latencySender <- admissionReviewLatencyDuration
2020-01-07 10:33:28 -08:00
}
2021-05-15 19:15:04 +05:30
func ( ws * WebhookServer ) registerPolicyRuleResultsMetricGeneration ( logger logr . Logger , resourceRequestOperation string , policy kyverno . ClusterPolicy , engineResponse response . EngineResponse , admissionRequestTimestamp int64 ) {
resourceRequestOperationPromAlias , err := policyRuleResults . ParseResourceRequestOperation ( resourceRequestOperation )
if err != nil {
logger . Error ( err , "error occurred while registering kyverno_policy_rule_results_info metrics for the above policy" , "name" , policy . Name )
}
if err := policyRuleResults . ParsePromMetrics ( * ws . promConfig . Metrics ) . ProcessEngineResponse ( policy , engineResponse , metrics . AdmissionRequest , resourceRequestOperationPromAlias , admissionRequestTimestamp ) ; err != nil {
logger . Error ( err , "error occurred while registering kyverno_policy_rule_results_info metrics for the above policy" , "name" , policy . Name )
}
}
2021-05-15 21:34:26 +05:30
func ( ws * WebhookServer ) registerPolicyRuleExecutionLatencyMetricGenerate ( logger logr . Logger , resourceRequestOperation string , policy kyverno . ClusterPolicy , engineResponse response . EngineResponse , admissionRequestTimestamp int64 ) {
resourceRequestOperationPromAlias , err := policyRuleExecutionLatency . ParseResourceRequestOperation ( resourceRequestOperation )
if err != nil {
logger . Error ( err , "error occurred while registering kyverno_policy_rule_execution_latency_milliseconds metrics for the above policy" , "name" , policy . Name )
}
if err := policyRuleExecutionLatency . ParsePromMetrics ( * ws . promConfig . Metrics ) . ProcessEngineResponse ( policy , engineResponse , metrics . AdmissionRequest , "" , resourceRequestOperationPromAlias , admissionRequestTimestamp ) ; err != nil {
logger . Error ( err , "error occurred while registering kyverno_policy_rule_execution_latency_milliseconds metrics for the above policy" , "name" , policy . Name )
}
}
2021-01-27 23:41:22 +05:30
//handleUpdate handles admission-requests for update
func ( ws * WebhookServer ) handleUpdate ( request * v1beta1 . AdmissionRequest , policies [ ] * kyverno . ClusterPolicy ) {
2021-05-13 12:03:13 -07:00
logger := ws . log . WithValues ( "action" , "generation" , "uid" , request . UID , "kind" , request . Kind , "namespace" , request . Namespace , "name" , request . Name , "operation" , request . Operation , "gvk" , request . Kind . String ( ) )
2020-12-23 23:44:17 +05:30
resource , err := enginutils . ConvertToUnstructured ( request . OldObject . Raw )
if err != nil {
logger . Error ( err , "failed to convert object resource to unstructured format" )
}
resLabels := resource . GetLabels ( )
2020-12-30 00:12:36 +05:30
if resLabels [ "generate.kyverno.io/clone-policy-name" ] != "" {
2021-01-27 23:41:22 +05:30
ws . handleUpdateCloneSourceResource ( resLabels , logger )
}
if resLabels [ "app.kubernetes.io/managed-by" ] == "kyverno" && resLabels [ "policy.kyverno.io/synchronize" ] == "enable" && request . Operation == v1beta1 . Update {
ws . handleUpdateTargetResource ( request , policies , resLabels , logger )
}
}
2021-03-22 19:14:06 -07:00
//handleUpdateCloneSourceResource - handles update of clone source for generate policy
2021-01-27 23:41:22 +05:30
func ( ws * WebhookServer ) handleUpdateCloneSourceResource ( resLabels map [ string ] string , logger logr . Logger ) {
policyNames := strings . Split ( resLabels [ "generate.kyverno.io/clone-policy-name" ] , "," )
for _ , policyName := range policyNames {
selector := labels . SelectorFromSet ( labels . Set ( map [ string ] string {
"generate.kyverno.io/policy-name" : policyName ,
} ) )
grList , err := ws . grLister . List ( selector )
if err != nil {
logger . Error ( err , "failed to get generate request for the resource" , "label" , "generate.kyverno.io/policy-name" )
2021-02-07 20:26:56 -08:00
return
2021-01-27 23:41:22 +05:30
}
for _ , gr := range grList {
ws . grController . EnqueueGenerateRequestFromWebhook ( gr )
}
}
}
2021-03-22 19:14:06 -07:00
//handleUpdateTargetResource - handles update of target resource for generate policy
2021-01-27 23:41:22 +05:30
func ( ws * WebhookServer ) handleUpdateTargetResource ( request * v1beta1 . AdmissionRequest , policies [ ] * v1 . ClusterPolicy , resLabels map [ string ] string , logger logr . Logger ) {
enqueueBool := false
newRes , err := enginutils . ConvertToUnstructured ( request . Object . Raw )
if err != nil {
logger . Error ( err , "failed to convert object resource to unstructured format" )
}
policyName := resLabels [ "policy.kyverno.io/policy-name" ]
targetSourceName := newRes . GetName ( )
targetSourceKind := newRes . GetKind ( )
2020-12-24 18:39:23 +05:30
2021-01-27 23:41:22 +05:30
for _ , policy := range policies {
if policy . GetName ( ) == policyName {
for _ , rule := range policy . Spec . Rules {
if rule . Generation . Kind == targetSourceKind && rule . Generation . Name == targetSourceName {
2021-04-27 01:28:34 +05:30
updatedRule , err := getGeneratedByResource ( newRes , resLabels , ws . client , rule , logger )
if err != nil {
logger . V ( 4 ) . Info ( "skipping generate policy and resource pattern validaton" , "error" , err )
} else {
data := updatedRule . Generation . DeepCopy ( ) . Data
if data != nil {
if _ , err := gen . ValidateResourceWithPattern ( logger , newRes . Object , data ) ; err != nil {
enqueueBool = true
break
}
2021-01-27 23:41:22 +05:30
}
2020-12-24 18:39:23 +05:30
2021-04-27 01:28:34 +05:30
cloneName := updatedRule . Generation . Clone . Name
if cloneName != "" {
obj , err := ws . client . GetResource ( "" , rule . Generation . Kind , rule . Generation . Clone . Namespace , rule . Generation . Clone . Name )
if err != nil {
logger . Error ( err , fmt . Sprintf ( "source resource %s/%s/%s not found." , rule . Generation . Kind , rule . Generation . Clone . Namespace , rule . Generation . Clone . Name ) )
continue
}
2021-01-27 23:41:22 +05:30
2021-04-27 01:28:34 +05:30
sourceObj , newResObj := stripNonPolicyFields ( obj . Object , newRes . Object , logger )
2021-01-27 23:41:22 +05:30
2021-04-27 01:28:34 +05:30
if _ , err := gen . ValidateResourceWithPattern ( logger , newResObj , sourceObj ) ; err != nil {
enqueueBool = true
break
}
2021-01-27 23:41:22 +05:30
}
}
}
2020-12-24 18:39:23 +05:30
}
2021-01-27 23:41:22 +05:30
}
}
if enqueueBool {
grName := resLabels [ "policy.kyverno.io/gr-name" ]
gr , err := ws . grLister . Get ( grName )
if err != nil {
logger . Error ( err , "failed to get generate request" , "name" , grName )
2021-02-07 20:26:56 -08:00
return
2021-01-27 23:41:22 +05:30
}
ws . grController . EnqueueGenerateRequestFromWebhook ( gr )
}
}
2021-04-27 01:28:34 +05:30
func getGeneratedByResource ( newRes * unstructured . Unstructured , resLabels map [ string ] string , client * client . Client , rule v1 . Rule , logger logr . Logger ) ( v1 . Rule , error ) {
var apiVersion , kind , name , namespace string
sourceRequest := & v1beta1 . AdmissionRequest { }
kind = resLabels [ "kyverno.io/generated-by-kind" ]
name = resLabels [ "kyverno.io/generated-by-name" ]
if kind != "Namespace" {
namespace = resLabels [ "kyverno.io/generated-by-namespace" ]
}
obj , err := client . GetResource ( apiVersion , kind , namespace , name )
if err != nil {
logger . Error ( err , "source resource not found." )
return rule , err
}
rawObj , err := json . Marshal ( obj )
if err != nil {
logger . Error ( err , "failed to marshal resource" )
return rule , err
}
sourceRequest . Object . Raw = rawObj
sourceRequest . Operation = "CREATE"
ctx := enginectx . NewContext ( )
if err := ctx . AddRequest ( sourceRequest ) ; err != nil {
logger . Error ( err , "failed to load incoming request in context" )
return rule , err
}
if rule , err = variables . SubstituteAllInRule ( logger , ctx , rule ) ; err != nil {
logger . Error ( err , "variable substitution failed for rule %s" , rule . Name )
return rule , err
}
return rule , nil
}
2021-01-27 23:41:22 +05:30
//stripNonPolicyFields - remove feilds which get updated with each request by kyverno and are non policy fields
func stripNonPolicyFields ( obj , newRes map [ string ] interface { } , logger logr . Logger ) ( map [ string ] interface { } , map [ string ] interface { } ) {
2021-02-10 23:58:50 +05:30
if metadata , found := obj [ "metadata" ] ; found {
2021-02-10 00:04:57 +05:30
requiredMetadataInObj := make ( map [ string ] interface { } )
2021-02-10 23:58:50 +05:30
if annotations , found := metadata . ( map [ string ] interface { } ) [ "annotations" ] ; found {
delete ( annotations . ( map [ string ] interface { } ) , "kubectl.kubernetes.io/last-applied-configuration" )
requiredMetadataInObj [ "annotations" ] = annotations
2021-02-10 00:04:57 +05:30
}
2021-02-10 23:58:50 +05:30
if labels , found := metadata . ( map [ string ] interface { } ) [ "labels" ] ; found {
delete ( labels . ( map [ string ] interface { } ) , "generate.kyverno.io/clone-policy-name" )
requiredMetadataInObj [ "labels" ] = labels
2021-02-10 00:04:57 +05:30
}
obj [ "metadata" ] = requiredMetadataInObj
2021-01-27 23:41:22 +05:30
}
2021-02-10 00:04:57 +05:30
2021-02-10 23:58:50 +05:30
if metadata , found := newRes [ "metadata" ] ; found {
2021-02-10 00:04:57 +05:30
requiredMetadataInNewRes := make ( map [ string ] interface { } )
2021-02-10 23:58:50 +05:30
if annotations , found := metadata . ( map [ string ] interface { } ) [ "annotations" ] ; found {
requiredMetadataInNewRes [ "annotations" ] = annotations
2021-02-10 00:04:57 +05:30
}
2021-02-10 23:58:50 +05:30
if labels , found := metadata . ( map [ string ] interface { } ) [ "labels" ] ; found {
requiredMetadataInNewRes [ "labels" ] = labels
2021-02-10 00:04:57 +05:30
}
newRes [ "metadata" ] = requiredMetadataInNewRes
2021-01-27 23:41:22 +05:30
}
if _ , found := obj [ "status" ] ; found {
delete ( obj , "status" )
}
if _ , found := obj [ "spec" ] ; found {
delete ( obj [ "spec" ] . ( map [ string ] interface { } ) , "tolerations" )
}
if dataMap , found := obj [ "data" ] ; found {
keyInData := make ( [ ] string , 0 )
switch dataMap . ( type ) {
case map [ string ] interface { } :
2021-02-08 10:49:25 +05:30
for k := range dataMap . ( map [ string ] interface { } ) {
2021-01-27 23:41:22 +05:30
keyInData = append ( keyInData , k )
2020-12-24 18:39:23 +05:30
}
}
2021-01-27 23:41:22 +05:30
if len ( keyInData ) > 0 {
for _ , dataKey := range keyInData {
originalResourceData := dataMap . ( map [ string ] interface { } ) [ dataKey ]
replaceData := strings . Replace ( originalResourceData . ( string ) , "\n" , "" , - 1 )
dataMap . ( map [ string ] interface { } ) [ dataKey ] = replaceData
newResourceData := newRes [ "data" ] . ( map [ string ] interface { } ) [ dataKey ]
replacenewResourceData := strings . Replace ( newResourceData . ( string ) , "\n" , "" , - 1 )
newRes [ "data" ] . ( map [ string ] interface { } ) [ dataKey ] = replacenewResourceData
}
} else {
logger . V ( 4 ) . Info ( "data is not of type map[string]interface{}" )
}
2020-12-24 18:39:23 +05:30
}
2021-01-27 23:41:22 +05:30
return obj , newRes
2020-12-24 18:39:23 +05:30
}
2020-12-30 00:12:36 +05:30
//HandleDelete handles admission-requests for delete
func ( ws * WebhookServer ) handleDelete ( request * v1beta1 . AdmissionRequest ) {
2021-05-13 12:03:13 -07:00
logger := ws . log . WithValues ( "action" , "generation" , "uid" , request . UID , "kind" , request . Kind , "namespace" , request . Namespace , "name" , request . Name , "operation" , request . Operation , "gvk" , request . Kind . String ( ) )
2020-12-30 00:12:36 +05:30
resource , err := enginutils . ConvertToUnstructured ( request . OldObject . Raw )
if err != nil {
logger . Error ( err , "failed to convert object resource to unstructured format" )
}
resLabels := resource . GetLabels ( )
if resLabels [ "app.kubernetes.io/managed-by" ] == "kyverno" && resLabels [ "policy.kyverno.io/synchronize" ] == "enable" && request . Operation == v1beta1 . Delete {
grName := resLabels [ "policy.kyverno.io/gr-name" ]
gr , err := ws . grLister . Get ( grName )
if err != nil {
logger . Error ( err , "failed to get generate request" , "name" , grName )
2021-02-07 20:26:56 -08:00
return
2020-12-30 00:12:36 +05:30
}
ws . grController . EnqueueGenerateRequestFromWebhook ( gr )
}
}
2020-12-24 12:41:54 +05:30
func ( ws * WebhookServer ) deleteGR ( logger logr . Logger , engineResponse * response . EngineResponse ) {
2020-12-16 12:29:16 -08:00
logger . V ( 4 ) . Info ( "querying all generate requests" )
selector := labels . SelectorFromSet ( labels . Set ( map [ string ] string {
2020-12-23 12:20:29 +05:30
"generate.kyverno.io/policy-name" : engineResponse . PolicyResponse . Policy ,
2020-12-23 13:06:48 +05:30
"generate.kyverno.io/resource-name" : engineResponse . PolicyResponse . Resource . Name ,
2020-12-23 12:20:29 +05:30
"generate.kyverno.io/resource-kind" : engineResponse . PolicyResponse . Resource . Kind ,
"generate.kyverno.io/resource-namespace" : engineResponse . PolicyResponse . Resource . Namespace ,
2020-12-16 12:29:16 -08:00
} ) )
grList , err := ws . grLister . List ( selector )
if err != nil {
logger . Error ( err , "failed to get generate request for the resource" , "kind" , engineResponse . PolicyResponse . Resource . Kind , "name" , engineResponse . PolicyResponse . Resource . Name , "namespace" , engineResponse . PolicyResponse . Resource . Namespace )
2021-02-07 20:26:56 -08:00
return
2020-12-16 12:29:16 -08:00
}
for _ , v := range grList {
err := ws . kyvernoClient . KyvernoV1 ( ) . GenerateRequests ( config . KyvernoNamespace ) . Delete ( contextdefault . TODO ( ) , v . GetName ( ) , metav1 . DeleteOptions { } )
if err != nil {
logger . Error ( err , "failed to update gr" )
}
}
}
2020-07-09 11:48:34 -07:00
func applyGenerateRequest ( gnGenerator generate . GenerateRequests , userRequestInfo kyverno . RequestInfo ,
2020-12-23 15:10:07 -08:00
action v1beta1 . Operation , engineResponses ... * response . EngineResponse ) ( failedGenerateRequest [ ] generateRequestResponse ) {
2020-07-09 11:48:34 -07:00
2020-01-07 10:33:28 -08:00
for _ , er := range engineResponses {
2020-07-09 11:48:34 -07:00
gr := transform ( userRequestInfo , er )
if err := gnGenerator . Apply ( gr , action ) ; err != nil {
failedGenerateRequest = append ( failedGenerateRequest , generateRequestResponse { gr : gr , err : err } )
2020-01-07 10:33:28 -08:00
}
}
2020-12-01 12:30:08 -08:00
2020-07-09 11:48:34 -07:00
return
2020-01-07 10:33:28 -08:00
}
2020-12-23 15:10:07 -08:00
func transform ( userRequestInfo kyverno . RequestInfo , er * response . EngineResponse ) kyverno . GenerateRequestSpec {
2020-01-07 10:33:28 -08:00
gr := kyverno . GenerateRequestSpec {
Policy : er . PolicyResponse . Policy ,
Resource : kyverno . ResourceSpec {
2021-03-05 06:15:52 +05:30
Kind : er . PolicyResponse . Resource . Kind ,
Namespace : er . PolicyResponse . Resource . Namespace ,
Name : er . PolicyResponse . Resource . Name ,
APIVersion : er . PolicyResponse . Resource . APIVersion ,
2020-01-07 10:33:28 -08:00
} ,
Context : kyverno . GenerateRequestContext {
UserRequestInfo : userRequestInfo ,
} ,
}
2020-12-23 15:10:07 -08:00
2020-01-07 10:33:28 -08:00
return gr
}
2020-02-29 22:39:27 +05:30
type generateStats struct {
2020-12-23 15:10:07 -08:00
resp * response . EngineResponse
2020-02-29 22:39:27 +05:30
}
2020-03-04 15:45:20 +05:30
func ( gs generateStats ) PolicyName ( ) string {
return gs . resp . PolicyResponse . Policy
2020-02-29 22:39:27 +05:30
}
2020-03-04 15:45:20 +05:30
func ( gs generateStats ) UpdateStatus ( status kyverno . PolicyStatus ) kyverno . PolicyStatus {
2020-02-29 22:39:27 +05:30
if reflect . DeepEqual ( response . EngineResponse { } , gs . resp ) {
2020-03-04 15:45:20 +05:30
return status
2020-02-29 22:39:27 +05:30
}
var nameToRule = make ( map [ string ] v1 . RuleStats )
for _ , rule := range status . Rules {
nameToRule [ rule . Name ] = rule
}
for _ , rule := range gs . resp . PolicyResponse . Rules {
ruleStat := nameToRule [ rule . Name ]
ruleStat . Name = rule . Name
averageOver := int64 ( ruleStat . AppliedCount + ruleStat . FailedCount )
ruleStat . ExecutionTime = updateAverageTime (
rule . ProcessingTime ,
ruleStat . ExecutionTime ,
averageOver ) . String ( )
if rule . Success {
status . RulesAppliedCount ++
ruleStat . AppliedCount ++
} else {
status . RulesFailedCount ++
ruleStat . FailedCount ++
}
nameToRule [ rule . Name ] = ruleStat
}
var policyAverageExecutionTime time . Duration
var ruleStats = make ( [ ] v1 . RuleStats , 0 , len ( nameToRule ) )
for _ , ruleStat := range nameToRule {
executionTime , err := time . ParseDuration ( ruleStat . ExecutionTime )
if err == nil {
policyAverageExecutionTime += executionTime
}
ruleStats = append ( ruleStats , ruleStat )
}
sort . Slice ( ruleStats , func ( i , j int ) bool {
return ruleStats [ i ] . Name < ruleStats [ j ] . Name
} )
status . AvgExecutionTime = policyAverageExecutionTime . String ( )
status . Rules = ruleStats
2020-03-04 15:45:20 +05:30
return status
2020-02-29 22:39:27 +05:30
}
func updateAverageTime ( newTime time . Duration , oldAverageTimeString string , averageOver int64 ) time . Duration {
if averageOver == 0 {
return newTime
}
oldAverageExecutionTime , _ := time . ParseDuration ( oldAverageTimeString )
numerator := ( oldAverageExecutionTime . Nanoseconds ( ) * averageOver ) + newTime . Nanoseconds ( )
denominator := averageOver + 1
newAverageTimeInNanoSeconds := numerator / denominator
return time . Duration ( newAverageTimeInNanoSeconds ) * time . Nanosecond
}
2020-07-09 11:48:34 -07:00
type generateRequestResponse struct {
gr v1 . GenerateRequestSpec
err error
}
func ( resp generateRequestResponse ) info ( ) string {
return strings . Join ( [ ] string { resp . gr . Resource . Kind , resp . gr . Resource . Namespace , resp . gr . Resource . Name } , "/" )
}
func ( resp generateRequestResponse ) error ( ) string {
return resp . err . Error ( )
}
func failedEvents ( err error , gr kyverno . GenerateRequestSpec , resource unstructured . Unstructured ) [ ] event . Info {
re := event . Info { }
re . Kind = resource . GetKind ( )
re . Namespace = resource . GetNamespace ( )
re . Name = resource . GetName ( )
re . Reason = event . PolicyFailed . String ( )
re . Source = event . GeneratePolicyController
re . Message = fmt . Sprintf ( "policy %s failed to apply: %v" , gr . Policy , err )
2020-07-20 08:00:02 -07:00
return [ ] event . Info { re }
2020-07-09 11:48:34 -07:00
}