2019-08-13 11:32:12 -07:00
package policy
import (
2019-08-26 13:34:42 -07:00
"fmt"
"reflect"
2019-08-13 11:32:12 -07:00
"time"
jsonpatch "github.com/evanphx/json-patch"
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
"github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/utils"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
// applyPolicy applies policy on a resource
//TODO: generation rules
2019-09-03 14:51:51 -07:00
func applyPolicy ( policy kyverno . ClusterPolicy , resource unstructured . Unstructured , policyStatus PolicyStatusInterface ) ( responses [ ] engine . EngineResponseNew ) {
2019-08-13 11:32:12 -07:00
startTime := time . Now ( )
2019-08-26 13:34:42 -07:00
var policyStats [ ] PolicyStat
2019-08-13 11:32:12 -07:00
glog . V ( 4 ) . Infof ( "Started apply policy %s on resource %s/%s/%s (%v)" , policy . Name , resource . GetKind ( ) , resource . GetNamespace ( ) , resource . GetName ( ) , startTime )
defer func ( ) {
glog . V ( 4 ) . Infof ( "Finished applying %s on resource %s/%s/%s (%v)" , policy . Name , resource . GetKind ( ) , resource . GetNamespace ( ) , resource . GetName ( ) , time . Since ( startTime ) )
} ( )
2019-08-26 13:34:42 -07:00
// gather stats from the engine response
gatherStat := func ( policyName string , policyResponse engine . PolicyResponse ) {
ps := PolicyStat { }
ps . PolicyName = policyName
ps . Stats . MutationExecutionTime = policyResponse . ProcessingTime
ps . Stats . RulesAppliedCount = policyResponse . RulesAppliedCount
policyStats = append ( policyStats , ps )
}
// send stats for aggregation
sendStat := func ( blocked bool ) {
for _ , stat := range policyStats {
stat . Stats . ResourceBlocked = utils . Btoi ( blocked )
//SEND
policyStatus . SendStat ( stat )
}
}
var engineResponses [ ] engine . EngineResponseNew
var engineResponse engine . EngineResponseNew
var err error
2019-08-13 17:24:05 -07:00
2019-08-13 11:32:12 -07:00
//MUTATION
2019-08-26 13:34:42 -07:00
engineResponse , err = mutation ( policy , resource , policyStatus )
engineResponses = append ( engineResponses , engineResponse )
2019-08-13 11:32:12 -07:00
if err != nil {
2019-08-30 01:08:54 -07:00
glog . Errorf ( "unable to process mutation rules: %v" , err )
2019-08-13 11:32:12 -07:00
}
2019-08-26 13:34:42 -07:00
gatherStat ( policy . Name , engineResponse . PolicyResponse )
//send stats
sendStat ( false )
2019-08-13 11:32:12 -07:00
//VALIDATION
2019-09-03 15:48:13 -07:00
engineResponse = engine . Validate ( policy , resource )
2019-08-26 13:34:42 -07:00
engineResponses = append ( engineResponses , engineResponse )
2019-08-20 16:57:19 -07:00
// gather stats
2019-08-26 13:34:42 -07:00
gatherStat ( policy . Name , engineResponse . PolicyResponse )
2019-08-20 16:57:19 -07:00
//send stats
sendStat ( false )
2019-08-13 11:32:12 -07:00
//TODO: GENERATION
2019-08-26 13:34:42 -07:00
return engineResponses
2019-08-13 11:32:12 -07:00
}
2019-09-03 14:51:51 -07:00
func mutation ( policy kyverno . ClusterPolicy , resource unstructured . Unstructured , policyStatus PolicyStatusInterface ) ( engine . EngineResponseNew , error ) {
2019-09-03 15:48:13 -07:00
engineResponse := engine . Mutate ( policy , resource )
2019-08-26 13:34:42 -07:00
if ! engineResponse . IsSuccesful ( ) {
glog . V ( 4 ) . Infof ( "mutation had errors reporting them" )
return engineResponse , nil
2019-08-20 16:57:19 -07:00
}
2019-08-26 13:34:42 -07:00
// Verify if the JSON pathes returned by the Mutate are already applied to the resource
if reflect . DeepEqual ( resource , engineResponse . PatchedResource ) {
// resources matches
glog . V ( 4 ) . Infof ( "resource %s/%s/%s satisfies policy %s" , engineResponse . PolicyResponse . Resource . Kind , engineResponse . PolicyResponse . Resource . Namespace , engineResponse . PolicyResponse . Resource . Name , engineResponse . PolicyResponse . Policy )
return engineResponse , nil
2019-08-20 16:57:19 -07:00
}
2019-08-26 13:34:42 -07:00
return getFailedOverallRuleInfo ( resource , engineResponse )
}
2019-08-20 16:57:19 -07:00
2019-08-26 13:34:42 -07:00
// getFailedOverallRuleInfo gets detailed info for over-all mutation failure
func getFailedOverallRuleInfo ( resource unstructured . Unstructured , engineResponse engine . EngineResponseNew ) ( engine . EngineResponseNew , error ) {
rawResource , err := resource . MarshalJSON ( )
if err != nil {
glog . V ( 4 ) . Infof ( "unable to marshal resource: %v\n" , err )
return engine . EngineResponseNew { } , err
2019-08-13 11:32:12 -07:00
}
2019-08-26 13:34:42 -07:00
// resource does not match so there was a mutation rule violated
for index , rule := range engineResponse . PolicyResponse . Rules {
2019-08-26 16:10:19 -07:00
glog . V ( 4 ) . Infof ( "veriying if policy %s rule %s was applied before to resource %s/%s/%s" , engineResponse . PolicyResponse . Policy , rule . Name , engineResponse . PolicyResponse . Resource . Kind , engineResponse . PolicyResponse . Resource . Namespace , engineResponse . PolicyResponse . Resource . Name )
2019-08-26 13:34:42 -07:00
if len ( rule . Patches ) == 0 {
continue
2019-08-13 11:32:12 -07:00
}
2019-08-26 13:34:42 -07:00
patch , err := jsonpatch . DecodePatch ( utils . JoinPatches ( rule . Patches ) )
if err != nil {
glog . V ( 4 ) . Infof ( "unable to decode patch %s: %v" , rule . Patches , err )
return engine . EngineResponseNew { } , err
}
2019-08-13 11:32:12 -07:00
2019-08-26 13:34:42 -07:00
// apply the patches returned by mutate to the original resource
patchedResource , err := patch . Apply ( rawResource )
if err != nil {
glog . V ( 4 ) . Infof ( "unable to apply patch %s: %v" , rule . Patches , err )
return engine . EngineResponseNew { } , err
}
2019-08-13 11:32:12 -07:00
2019-08-26 13:34:42 -07:00
if ! jsonpatch . Equal ( patchedResource , rawResource ) {
glog . V ( 4 ) . Infof ( "policy %s rule %s condition not satisifed by existing resource" , engineResponse . PolicyResponse . Policy , rule . Name )
engineResponse . PolicyResponse . Rules [ index ] . Success = false
2019-08-26 16:10:19 -07:00
engineResponse . PolicyResponse . Rules [ index ] . Message = fmt . Sprintf ( "rule not satisfied by existing resource." )
2019-08-26 13:34:42 -07:00
}
2019-08-13 11:32:12 -07:00
}
2019-08-26 13:34:42 -07:00
return engineResponse , nil
2019-08-13 11:32:12 -07:00
}