2019-08-13 09:37:02 -07:00
package policy
import (
"sync"
"time"
2020-03-17 11:05:20 -07:00
"github.com/go-logr/logr"
2020-10-07 11:12:31 -07:00
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine"
"github.com/kyverno/kyverno/pkg/engine/response"
2019-08-13 09:37:02 -07:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
2020-06-25 09:52:54 -07:00
func ( pc * PolicyController ) processExistingResources ( policy * kyverno . ClusterPolicy ) [ ] response . EngineResponse {
2020-03-17 11:05:20 -07:00
logger := pc . log . WithValues ( "policy" , policy . Name )
2019-08-13 09:37:02 -07:00
// Parse through all the resources
// drops the cache after configured rebuild time
pc . rm . Drop ( )
2019-12-12 15:02:59 -08:00
var engineResponses [ ] response . EngineResponse
2019-08-13 09:37:02 -07:00
// get resource that are satisfy the resource description defined in the rules
2020-05-26 10:36:56 -07:00
resourceMap := pc . listResources ( policy )
2019-08-13 09:37:02 -07:00
for _ , resource := range resourceMap {
// pre-processing, check if the policy and resource version has been processed before
if ! pc . rm . ProcessResource ( policy . Name , policy . ResourceVersion , resource . GetKind ( ) , resource . GetNamespace ( ) , resource . GetName ( ) , resource . GetResourceVersion ( ) ) {
2020-03-17 11:05:20 -07:00
logger . V ( 4 ) . Info ( "policy and resource already processed" , "policyResourceVersion" , policy . ResourceVersion , "resourceResourceVersion" , resource . GetResourceVersion ( ) , "kind" , resource . GetKind ( ) , "namespace" , resource . GetNamespace ( ) , "name" , resource . GetName ( ) )
2019-08-13 09:37:02 -07:00
continue
}
2019-12-26 18:41:14 -08:00
2019-08-13 09:37:02 -07:00
// apply the policy on each
2020-09-23 02:41:49 +05:30
engineResponse := applyPolicy ( * policy , resource , logger , pc . configHandler . GetExcludeGroupRole ( ) , pc . resCache )
2020-01-24 12:05:53 -08:00
// get engine response for mutation & validation independently
2019-08-26 13:34:42 -07:00
engineResponses = append ( engineResponses , engineResponse ... )
2019-08-13 09:37:02 -07:00
// post-processing, register the resource as processed
pc . rm . RegisterResource ( policy . GetName ( ) , policy . GetResourceVersion ( ) , resource . GetKind ( ) , resource . GetNamespace ( ) , resource . GetName ( ) , resource . GetResourceVersion ( ) )
}
2019-08-26 13:34:42 -07:00
return engineResponses
2019-08-13 11:32:12 -07:00
}
2020-06-25 09:52:54 -07:00
func ( pc * PolicyController ) listResources ( policy * kyverno . ClusterPolicy ) map [ string ] unstructured . Unstructured {
pc . log . V ( 4 ) . Info ( "list resources to be processed" )
2019-08-13 09:37:02 -07:00
// key uid
resourceMap := map [ string ] unstructured . Unstructured { }
for _ , rule := range policy . Spec . Rules {
for _ , k := range rule . MatchResources . Kinds {
2020-05-17 09:51:18 -07:00
2020-08-07 09:47:33 +05:30
resourceSchema , _ , err := pc . client . DiscoveryClient . FindResource ( "" , k )
2020-05-17 09:51:18 -07:00
if err != nil {
2020-05-26 10:36:56 -07:00
pc . log . Error ( err , "failed to find resource" , "kind" , k )
2020-05-17 09:51:18 -07:00
continue
2019-08-13 09:37:02 -07:00
}
2020-05-17 09:51:18 -07:00
if ! resourceSchema . Namespaced {
2020-08-28 16:41:16 +05:30
rMap := GetResourcesPerNamespace ( k , pc . client , "" , rule , pc . configHandler , pc . log )
MergeResources ( resourceMap , rMap )
2020-05-17 09:51:18 -07:00
} else {
2020-08-28 16:41:16 +05:30
namespaces := GetNamespacesForRule ( & rule , pc . nsLister , pc . log )
2020-05-17 09:51:18 -07:00
for _ , ns := range namespaces {
2020-08-28 16:41:16 +05:30
rMap := GetResourcesPerNamespace ( k , pc . client , ns , rule , pc . configHandler , pc . log )
MergeResources ( resourceMap , rMap )
2020-05-17 09:51:18 -07:00
}
}
2019-08-13 09:37:02 -07:00
}
}
2020-05-17 09:51:18 -07:00
2020-09-01 09:11:20 -07:00
return excludeAutoGenResources ( * policy , resourceMap , pc . log )
2020-06-25 09:52:54 -07:00
}
2020-09-01 09:11:20 -07:00
// excludeAutoGenResources filter out the pods / jobs with ownerReference
func excludeAutoGenResources ( policy kyverno . ClusterPolicy , resourceMap map [ string ] unstructured . Unstructured , log logr . Logger ) map [ string ] unstructured . Unstructured {
2020-06-25 09:52:54 -07:00
for uid , r := range resourceMap {
2020-09-01 09:11:20 -07:00
if engine . SkipPolicyApplication ( policy , r ) {
log . V ( 4 ) . Info ( "exclude resource" , "namespace" , r . GetNamespace ( ) , "kind" , r . GetKind ( ) , "name" , r . GetName ( ) )
2020-06-25 09:52:54 -07:00
delete ( resourceMap , uid )
}
}
2019-08-13 09:37:02 -07:00
return resourceMap
}
2020-01-24 12:05:53 -08:00
//Condition defines condition type
2019-09-03 19:31:42 -07:00
type Condition int
const (
2020-01-24 12:05:53 -08:00
//NotEvaluate to not evaluate condition
2019-09-03 19:31:42 -07:00
NotEvaluate Condition = 0
2020-01-24 12:05:53 -08:00
// Process to evaluate condition
Process Condition = 1
// Skip to ignore/skip the condition
Skip Condition = 2
2019-09-03 19:31:42 -07:00
)
2019-08-13 09:37:02 -07:00
// merge b into a map
2020-05-26 10:36:56 -07:00
func mergeResources ( a , b map [ string ] unstructured . Unstructured ) {
2019-08-13 09:37:02 -07:00
for k , v := range b {
a [ k ] = v
}
}
2019-08-14 10:01:47 -07:00
//NewResourceManager returns a new ResourceManager
2019-08-13 09:37:02 -07:00
func NewResourceManager ( rebuildTime int64 ) * ResourceManager {
rm := ResourceManager {
data : make ( map [ string ] interface { } ) ,
time : time . Now ( ) ,
rebuildTime : rebuildTime ,
}
// set time it was built
return & rm
}
2019-08-14 10:01:47 -07:00
// ResourceManager stores the details on already processed resources for caching
2019-08-13 09:37:02 -07:00
type ResourceManager struct {
// we drop and re-build the cache
// based on the memory consumer of by the map
data map [ string ] interface { }
mux sync . RWMutex
time time . Time
rebuildTime int64 // after how many seconds should we rebuild the cache
}
type resourceManager interface {
ProcessResource ( policy , pv , kind , ns , name , rv string ) bool
//TODO removeResource(kind, ns, name string) error
RegisterResource ( policy , pv , kind , ns , name , rv string )
// reload
Drop ( )
}
//Drop drop the cache after every rebuild interval mins
//TODO: or drop based on the size
func ( rm * ResourceManager ) Drop ( ) {
2019-08-13 10:03:00 -07:00
timeSince := time . Since ( rm . time )
if timeSince > time . Duration ( rm . rebuildTime ) * time . Second {
2019-08-13 09:37:02 -07:00
rm . mux . Lock ( )
defer rm . mux . Unlock ( )
rm . data = map [ string ] interface { } { }
2019-08-13 10:03:00 -07:00
rm . time = time . Now ( )
2019-08-13 09:37:02 -07:00
}
}
var empty struct { }
//RegisterResource stores if the policy is processed on this resource version
func ( rm * ResourceManager ) RegisterResource ( policy , pv , kind , ns , name , rv string ) {
rm . mux . Lock ( )
defer rm . mux . Unlock ( )
// add the resource
key := buildKey ( policy , pv , kind , ns , name , rv )
rm . data [ key ] = empty
}
//ProcessResource returns true if the policy was not applied on the resource
func ( rm * ResourceManager ) ProcessResource ( policy , pv , kind , ns , name , rv string ) bool {
rm . mux . RLock ( )
defer rm . mux . RUnlock ( )
key := buildKey ( policy , pv , kind , ns , name , rv )
_ , ok := rm . data [ key ]
2020-01-27 08:58:53 -08:00
return ! ok
2019-08-13 09:37:02 -07:00
}
func buildKey ( policy , pv , kind , ns , name , rv string ) string {
return policy + "/" + pv + "/" + kind + "/" + ns + "/" + name + "/" + rv
}