2019-07-15 16:07:56 -07:00
package webhooks
import (
2020-02-29 22:39:27 +05:30
"reflect"
"sort"
2020-01-16 11:57:28 -08:00
"time"
2019-07-15 16:07:56 -07:00
"github.com/golang/glog"
2019-11-13 13:41:08 -08:00
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
2020-02-29 22:39:27 +05:30
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
2020-01-07 17:06:17 -08:00
"github.com/nirmata/kyverno/pkg/engine"
2019-12-30 17:08:50 -08:00
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/engine/response"
2020-01-07 17:06:17 -08:00
engineutils "github.com/nirmata/kyverno/pkg/engine/utils"
2020-01-09 12:24:37 -08:00
"github.com/nirmata/kyverno/pkg/policyviolation"
2019-07-15 16:07:56 -07:00
v1beta1 "k8s.io/api/admission/v1beta1"
2020-01-06 19:24:24 -08:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2019-07-15 16:07:56 -07:00
)
// HandleMutation handles mutating webhook admission request
2020-01-16 14:29:44 -08:00
// return value: generated patches
func ( ws * WebhookServer ) HandleMutation ( request * v1beta1 . AdmissionRequest , resource unstructured . Unstructured , policies [ ] kyverno . ClusterPolicy , roles , clusterRoles [ ] string ) [ ] byte {
2019-08-23 18:34:23 -07:00
glog . V ( 4 ) . Infof ( "Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s" ,
request . Kind . Kind , request . Namespace , request . Name , request . UID , request . Operation )
2019-08-09 12:59:37 -07:00
var patches [ ] [ ] byte
2019-12-30 17:08:50 -08:00
var engineResponses [ ] response . EngineResponse
2020-01-07 10:33:28 -08:00
userRequestInfo := kyverno . RequestInfo {
Roles : roles ,
ClusterRoles : clusterRoles ,
AdmissionUserInfo : request . UserInfo }
2019-12-30 17:08:50 -08:00
// build context
ctx := context . NewContext ( )
2020-01-24 16:27:51 -08:00
var err error
2019-12-30 17:08:50 -08:00
// load incoming resource into the context
2020-01-24 16:27:51 -08:00
err = ctx . AddResource ( request . Object . Raw )
if err != nil {
glog . Infof ( "Failed to load resource in context:%v" , err )
}
err = ctx . AddUserInfo ( userRequestInfo )
if err != nil {
glog . Infof ( "Failed to load userInfo in context:%v" , err )
}
err = ctx . AddSA ( userRequestInfo . AdmissionUserInfo . Username )
if err != nil {
glog . Infof ( "Failed to load service account in context:%v" , err )
}
2020-01-07 15:13:57 -08:00
2019-11-08 18:57:27 -08:00
policyContext := engine . PolicyContext {
2020-01-07 14:17:25 -08:00
NewResource : resource ,
2020-01-07 10:33:28 -08:00
AdmissionInfo : userRequestInfo ,
2020-01-10 18:25:16 -08:00
Context : ctx ,
2019-11-08 18:57:27 -08:00
}
2019-08-14 11:51:01 -07:00
2019-11-08 18:57:27 -08:00
for _ , policy := range policies {
2019-10-23 23:18:58 -07:00
glog . V ( 2 ) . Infof ( "Handling mutation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s" ,
2019-08-09 12:59:37 -07:00
resource . GetKind ( ) , resource . GetNamespace ( ) , resource . GetName ( ) , request . UID , request . Operation )
2019-11-13 11:46:46 -08:00
policyContext . Policy = policy
2019-11-08 18:57:27 -08:00
engineResponse := engine . Mutate ( policyContext )
2019-08-23 18:34:23 -07:00
engineResponses = append ( engineResponses , engineResponse )
2020-03-04 15:45:20 +05:30
ws . status . Listener <- mutateStats {
resp : engineResponse ,
}
2019-08-23 18:34:23 -07:00
if ! engineResponse . IsSuccesful ( ) {
2019-08-14 19:00:37 -07:00
glog . V ( 4 ) . Infof ( "Failed to apply policy %s on resource %s/%s\n" , policy . Name , resource . GetNamespace ( ) , resource . GetName ( ) )
2019-08-09 12:59:37 -07:00
continue
2019-07-15 16:07:56 -07:00
}
2019-08-23 18:34:23 -07:00
// gather patches
2019-08-26 16:10:19 -07:00
patches = append ( patches , engineResponse . GetPatches ( ) ... )
2020-01-24 12:05:53 -08:00
glog . V ( 4 ) . Infof ( "Mutation from policy %s has applied successfully to %s %s/%s" , policy . Name , request . Kind . Kind , resource . GetNamespace ( ) , resource . GetName ( ) )
2020-01-15 18:15:48 -08:00
policyContext . NewResource = engineResponse . PatchedResource
2019-07-15 16:07:56 -07:00
}
2019-10-07 18:31:14 -07:00
// generate annotations
2019-11-11 18:52:26 -08:00
if annPatches := generateAnnotationPatches ( engineResponses ) ; annPatches != nil {
2019-10-07 18:31:14 -07:00
patches = append ( patches , annPatches )
}
2020-01-16 11:57:28 -08:00
// report time
reportTime := time . Now ( )
// AUDIT
// generate violation when response fails
2020-01-09 12:24:37 -08:00
pvInfos := policyviolation . GeneratePVsFromEngineResponse ( engineResponses )
ws . pvGenerator . Add ( pvInfos ... )
2020-02-19 19:24:34 -08:00
// REPORTING EVENTS
// Scenario 1:
// some/all policies failed to apply on the resource. a policy volation is generated.
// create an event on the resource and the policy that failed
// Scenario 2:
// all policies were applied succesfully.
// create an event on the resource
2019-08-09 12:59:37 -07:00
// ADD EVENTS
2020-02-19 19:24:34 -08:00
events := generateEvents ( engineResponses , false , ( request . Operation == v1beta1 . Update ) )
2019-08-26 13:34:42 -07:00
ws . eventGen . Add ( events ... )
2020-01-15 21:46:58 -08:00
// debug info
2020-01-16 11:57:28 -08:00
func ( ) {
if len ( patches ) != 0 {
2020-01-16 14:37:01 -08:00
glog . V ( 4 ) . Infof ( "Patches generated for %s/%s/%s, operation=%v:\n %v" ,
2020-01-16 11:57:28 -08:00
resource . GetKind ( ) , resource . GetNamespace ( ) , resource . GetName ( ) , request . Operation , string ( engineutils . JoinPatches ( patches ) ) )
}
2020-01-15 21:46:58 -08:00
2020-01-16 11:57:28 -08:00
// if any of the policies fails, print out the error
if ! isResponseSuccesful ( engineResponses ) {
glog . Errorf ( "Failed to mutate the resource, report as violation: %s\n" , getErrorMsg ( engineResponses ) )
}
} ( )
// report time end
glog . V ( 4 ) . Infof ( "report: %v %s/%s/%s" , time . Since ( reportTime ) , resource . GetKind ( ) , resource . GetNamespace ( ) , resource . GetName ( ) )
2019-08-14 11:51:01 -07:00
2020-01-16 11:57:28 -08:00
// patches holds all the successful patches, if no patch is created, it returns nil
2020-01-16 14:29:44 -08:00
return engineutils . JoinPatches ( patches )
2019-07-15 16:07:56 -07:00
}
2020-02-29 22:39:27 +05:30
type mutateStats struct {
resp response . EngineResponse
}
2020-03-04 15:45:20 +05:30
func ( ms mutateStats ) PolicyName ( ) string {
return ms . resp . PolicyResponse . Policy
2020-02-29 22:39:27 +05:30
}
2020-03-04 15:45:20 +05:30
func ( ms mutateStats ) UpdateStatus ( status kyverno . PolicyStatus ) kyverno . PolicyStatus {
2020-02-29 22:39:27 +05:30
if reflect . DeepEqual ( response . EngineResponse { } , ms . 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 ms . 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 ++
status . ResourcesMutatedCount ++
ruleStat . AppliedCount ++
ruleStat . ResourcesMutatedCount ++
} 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
}