diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go index 68d1bbd857..98f2c735ab 100644 --- a/cmd/kyverno/main.go +++ b/cmd/kyverno/main.go @@ -5,6 +5,8 @@ import ( "flag" "time" + "github.com/nirmata/kyverno/pkg/policyStatus" + "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/checker" kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" @@ -136,7 +138,7 @@ func main() { pInformer.Kyverno().V1().ClusterPolicies()) // Policy Status Handler - deals with all logic related to policy status - statusSync := policy.NewStatusSync( + statusSync := policyStatus.NewSync( pclient, stopCh, policyMetaStore) diff --git a/pkg/generate/controller.go b/pkg/generate/controller.go index 31e12a68c4..e5f4c2a8e8 100644 --- a/pkg/generate/controller.go +++ b/pkg/generate/controller.go @@ -4,8 +4,6 @@ import ( "fmt" "time" - "github.com/nirmata/kyverno/pkg/policy" - "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" @@ -13,6 +11,7 @@ import ( kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1" dclient "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/event" + "github.com/nirmata/kyverno/pkg/policyStatus" "github.com/nirmata/kyverno/pkg/policyviolation" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -70,7 +69,7 @@ func NewController( eventGen event.Interface, pvGenerator policyviolation.GeneratorInterface, dynamicInformer dynamicinformer.DynamicSharedInformerFactory, - policyStatus *policy.StatSync, + policyStatus *policyStatus.Sync, ) *Controller { c := Controller{ client: client, diff --git a/pkg/generate/status.go b/pkg/generate/status.go index 026a657457..6f14597229 100644 --- a/pkg/generate/status.go +++ b/pkg/generate/status.go @@ -4,7 +4,7 @@ import ( "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" - "github.com/nirmata/kyverno/pkg/policy" + "github.com/nirmata/kyverno/pkg/policyStatus" ) //StatusControlInterface provides interface to update status subresource @@ -16,7 +16,7 @@ type StatusControlInterface interface { // StatusControl is default implementaation of GRStatusControlInterface type StatusControl struct { client kyvernoclient.Interface - policyStatus *policy.StatSync + policyStatus *policyStatus.Sync } //Failed sets gr status.state to failed with message diff --git a/pkg/policy/status.go b/pkg/policy/status.go deleted file mode 100644 index c7aceb9dbc..0000000000 --- a/pkg/policy/status.go +++ /dev/null @@ -1,308 +0,0 @@ -package policy - -import ( - "log" - "reflect" - "sort" - "sync" - "time" - - "github.com/nirmata/kyverno/pkg/policystore" - - "github.com/nirmata/kyverno/pkg/engine/response" - - "k8s.io/apimachinery/pkg/util/wait" - - "github.com/nirmata/kyverno/pkg/client/clientset/versioned" - - v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" -) - -type statusCache struct { - mu sync.RWMutex - data map[string]v1.PolicyStatus -} - -type StatSync struct { - cache *statusCache - stop <-chan struct{} - client *versioned.Clientset - policyStore *policystore.PolicyStore -} - -func NewStatusSync( - client *versioned.Clientset, - stopCh <-chan struct{}, - pMetaStore *policystore.PolicyStore, -) *StatSync { - return &StatSync{ - cache: &statusCache{ - mu: sync.RWMutex{}, - data: make(map[string]v1.PolicyStatus), - }, - stop: stopCh, - client: client, - policyStore: pMetaStore, - } -} - -func (s *StatSync) Run() { - // update policy status every 10 seconds - waits for previous updateStatus to complete - wait.Until(s.updateStats, 1*time.Second, s.stop) - <-s.stop - s.updateStats() -} - -func (s *StatSync) updateStats() { - s.cache.mu.Lock() - var nameToStatus = make(map[string]v1.PolicyStatus, len(s.cache.data)) - for k, v := range s.cache.data { - nameToStatus[k] = v - } - s.cache.data = make(map[string]v1.PolicyStatus) - s.cache.mu.Unlock() - - for policyName, status := range nameToStatus { - var policy = &v1.ClusterPolicy{} - policy, err := s.policyStore.Get(policyName) - if err != nil { - continue - } - policy.Status = status - _, err = s.client.KyvernoV1().ClusterPolicies().UpdateStatus(policy) - if err != nil { - log.Println(err) - } - } -} - -func (s *StatSync) UpdatePolicyStatusWithViolationCount(policyName string, violatedRules []v1.ViolatedRule) { - s.cache.mu.Lock() - status := s.cache.data[policyName] - - var ruleNameToViolations = make(map[string]int) - for _, rule := range violatedRules { - ruleNameToViolations[rule.Name]++ - } - - for i := range status.Rules { - status.ViolationCount += ruleNameToViolations[status.Rules[i].Name] - status.Rules[i].ViolationCount += ruleNameToViolations[status.Rules[i].Name] - } - - s.cache.data[policyName] = status - s.cache.mu.Unlock() -} - -func (s *StatSync) UpdatePolicyStatusWithGeneratedResourceCount(generateRequest v1.GenerateRequest) { - s.cache.mu.Lock() - status := s.cache.data[generateRequest.Spec.Policy] - - status.ResourcesGeneratedCount += len(generateRequest.Status.GeneratedResources) - - s.cache.data[generateRequest.Spec.Policy] = status - s.cache.mu.Unlock() -} - -func (s *StatSync) UpdateStatusWithMutateStats(resp response.EngineResponse) { - if reflect.DeepEqual(response.EngineResponse{}, resp) { - return - } - - s.cache.mu.Lock() - var policyStatus v1.PolicyStatus - policyStatus, exist := s.cache.data[resp.PolicyResponse.Policy] - if !exist { - policy, _ := s.policyStore.Get(resp.PolicyResponse.Policy) - if policy != nil { - policyStatus = policy.Status - } - } - - var nameToRule = make(map[string]v1.RuleStats, 0) - for _, rule := range policyStatus.Rules { - nameToRule[rule.Name] = rule - } - - for _, rule := range 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 { - policyStatus.RulesAppliedCount++ - policyStatus.ResourcesMutatedCount++ - ruleStat.AppliedCount++ - ruleStat.ResourcesMutatedCount++ - } else { - policyStatus.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 - }) - - policyStatus.AvgExecutionTime = policyAverageExecutionTime.String() - policyStatus.Rules = ruleStats - - s.cache.data[resp.PolicyResponse.Policy] = policyStatus - s.cache.mu.Unlock() -} - -func (s *StatSync) UpdateStatusWithValidateStats(resp response.EngineResponse) { - if reflect.DeepEqual(response.EngineResponse{}, resp) { - return - } - - s.cache.mu.Lock() - var policyStatus v1.PolicyStatus - policyStatus, exist := s.cache.data[resp.PolicyResponse.Policy] - if !exist { - policy, _ := s.policyStore.Get(resp.PolicyResponse.Policy) - if policy != nil { - policyStatus = policy.Status - } - } - - var nameToRule = make(map[string]v1.RuleStats, 0) - for _, rule := range policyStatus.Rules { - nameToRule[rule.Name] = rule - } - - for _, rule := range 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 { - policyStatus.RulesAppliedCount++ - ruleStat.AppliedCount++ - } else { - policyStatus.RulesFailedCount++ - ruleStat.FailedCount++ - if resp.PolicyResponse.ValidationFailureAction == "enforce" { - policyStatus.ResourcesBlockedCount++ - ruleStat.ResourcesBlockedCount++ - } - } - - 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 - }) - - policyStatus.AvgExecutionTime = policyAverageExecutionTime.String() - policyStatus.Rules = ruleStats - - s.cache.data[resp.PolicyResponse.Policy] = policyStatus - s.cache.mu.Unlock() -} - -func (s *StatSync) UpdateStatusWithGenerateStats(resp response.EngineResponse) { - if reflect.DeepEqual(response.EngineResponse{}, resp) { - return - } - - s.cache.mu.Lock() - var policyStatus v1.PolicyStatus - policyStatus, exist := s.cache.data[resp.PolicyResponse.Policy] - if !exist { - policy, _ := s.policyStore.Get(resp.PolicyResponse.Policy) - if policy != nil { - policyStatus = policy.Status - } - } - - var nameToRule = make(map[string]v1.RuleStats, 0) - for _, rule := range policyStatus.Rules { - nameToRule[rule.Name] = rule - } - - for _, rule := range 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 { - policyStatus.RulesAppliedCount++ - ruleStat.AppliedCount++ - } else { - policyStatus.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 - }) - - policyStatus.AvgExecutionTime = policyAverageExecutionTime.String() - policyStatus.Rules = ruleStats - - s.cache.data[resp.PolicyResponse.Policy] = policyStatus - s.cache.mu.Unlock() -} - -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 -} diff --git a/pkg/policyStatus/0_main.go b/pkg/policyStatus/0_main.go new file mode 100644 index 0000000000..aa008844db --- /dev/null +++ b/pkg/policyStatus/0_main.go @@ -0,0 +1,81 @@ +package policyStatus + +import ( + "sync" + "time" + + "github.com/golang/glog" + + "github.com/nirmata/kyverno/pkg/policystore" + + "k8s.io/apimachinery/pkg/util/wait" + + "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + + v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" +) + +type Sync struct { + cache *cache + listener chan statusUpdater + stop <-chan struct{} + client *versioned.Clientset + policyStore *policystore.PolicyStore +} + +type cache struct { + mutex sync.RWMutex + data map[string]v1.PolicyStatus +} + +func NewSync(c *versioned.Clientset, sc <-chan struct{}, pms *policystore.PolicyStore) *Sync { + return &Sync{ + cache: &cache{ + mutex: sync.RWMutex{}, + data: make(map[string]v1.PolicyStatus), + }, + stop: sc, + client: c, + policyStore: pms, + } +} + +func (s *Sync) Run() { + wait.Until(s.updatePolicyStatus, 5*time.Second, s.stop) + <-s.stop + s.updatePolicyStatus() +} + +func (s *Sync) updateStatusCache() { + for { + select { + case statusUpdater := <-s.listener: + statusUpdater.updateStatus() + case <-s.stop: + return + } + } +} + +func (s *Sync) updatePolicyStatus() { + s.cache.mutex.Lock() + var nameToStatus = make(map[string]v1.PolicyStatus, len(s.cache.data)) + for k, v := range s.cache.data { + nameToStatus[k] = v + } + s.cache.data = make(map[string]v1.PolicyStatus) + s.cache.mutex.Unlock() + + for policyName, status := range nameToStatus { + var policy = &v1.ClusterPolicy{} + policy, err := s.policyStore.Get(policyName) + if err != nil { + continue + } + policy.Status = status + _, err = s.client.KyvernoV1().ClusterPolicies().UpdateStatus(policy) + if err != nil { + glog.V(4).Info(err) + } + } +} diff --git a/pkg/policyStatus/1_interface.go b/pkg/policyStatus/1_interface.go new file mode 100644 index 0000000000..942337fb8a --- /dev/null +++ b/pkg/policyStatus/1_interface.go @@ -0,0 +1,5 @@ +package policyStatus + +type statusUpdater interface { + updateStatus() +} diff --git a/pkg/policyStatus/2_functions.go b/pkg/policyStatus/2_functions.go new file mode 100644 index 0000000000..db18b57ea8 --- /dev/null +++ b/pkg/policyStatus/2_functions.go @@ -0,0 +1,14 @@ +package policyStatus + +import "time" + +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 +} diff --git a/pkg/policyStatus/generateCount.go b/pkg/policyStatus/generateCount.go new file mode 100644 index 0000000000..5c633d9eaa --- /dev/null +++ b/pkg/policyStatus/generateCount.go @@ -0,0 +1,84 @@ +package policyStatus + +import ( + "reflect" + "sort" + "time" + + v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + "github.com/nirmata/kyverno/pkg/engine/response" +) + +type generateStats struct { + s *Sync + resp response.EngineResponse +} + +func (s *Sync) UpdateStatusWithGenerateStats(resp response.EngineResponse) { + s.listener <- &generateStats{ + s: s, + resp: resp, + } +} + +func (gs *generateStats) updateStatus() { + if reflect.DeepEqual(response.EngineResponse{}, gs.resp) { + return + } + + gs.s.cache.mutex.Lock() + var policyStatus v1.PolicyStatus + policyStatus, exist := gs.s.cache.data[gs.resp.PolicyResponse.Policy] + if !exist { + policy, _ := gs.s.policyStore.Get(gs.resp.PolicyResponse.Policy) + if policy != nil { + policyStatus = policy.Status + } + } + + var nameToRule = make(map[string]v1.RuleStats, 0) + for _, rule := range policyStatus.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 { + policyStatus.RulesAppliedCount++ + ruleStat.AppliedCount++ + } else { + policyStatus.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 + }) + + policyStatus.AvgExecutionTime = policyAverageExecutionTime.String() + policyStatus.Rules = ruleStats + + gs.s.cache.data[gs.resp.PolicyResponse.Policy] = policyStatus + gs.s.cache.mutex.Unlock() +} diff --git a/pkg/policyStatus/generatedResourceCount.go b/pkg/policyStatus/generatedResourceCount.go new file mode 100644 index 0000000000..9f5d013437 --- /dev/null +++ b/pkg/policyStatus/generatedResourceCount.go @@ -0,0 +1,25 @@ +package policyStatus + +import v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + +type generatedResourceCount struct { + sync *Sync + generateRequest v1.GenerateRequest +} + +func (s *Sync) UpdatePolicyStatusWithGeneratedResourceCount(generateRequest v1.GenerateRequest) { + s.listener <- &generatedResourceCount{ + sync: s, + generateRequest: generateRequest, + } +} + +func (vc *generatedResourceCount) updateStatus() { + vc.sync.cache.mutex.Lock() + status := vc.sync.cache.data[vc.generateRequest.Spec.Policy] + + status.ResourcesGeneratedCount += len(vc.generateRequest.Status.GeneratedResources) + + vc.sync.cache.data[vc.generateRequest.Spec.Policy] = status + vc.sync.cache.mutex.Unlock() +} diff --git a/pkg/policyStatus/mutateStats.go b/pkg/policyStatus/mutateStats.go new file mode 100644 index 0000000000..06052f0351 --- /dev/null +++ b/pkg/policyStatus/mutateStats.go @@ -0,0 +1,87 @@ +package policyStatus + +import ( + "reflect" + "sort" + "time" + + v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + "github.com/nirmata/kyverno/pkg/engine/response" +) + +type mutateStats struct { + s *Sync + resp response.EngineResponse +} + +func (s *Sync) UpdateStatusWithMutateStats(resp response.EngineResponse) { + s.listener <- &mutateStats{ + s: s, + resp: resp, + } + +} + +func (ms *mutateStats) updateStatus() { + if reflect.DeepEqual(response.EngineResponse{}, ms.resp) { + return + } + + ms.s.cache.mutex.Lock() + var policyStatus v1.PolicyStatus + policyStatus, exist := ms.s.cache.data[ms.resp.PolicyResponse.Policy] + if !exist { + policy, _ := ms.s.policyStore.Get(ms.resp.PolicyResponse.Policy) + if policy != nil { + policyStatus = policy.Status + } + } + + var nameToRule = make(map[string]v1.RuleStats, 0) + for _, rule := range policyStatus.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 { + policyStatus.RulesAppliedCount++ + policyStatus.ResourcesMutatedCount++ + ruleStat.AppliedCount++ + ruleStat.ResourcesMutatedCount++ + } else { + policyStatus.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 + }) + + policyStatus.AvgExecutionTime = policyAverageExecutionTime.String() + policyStatus.Rules = ruleStats + + ms.s.cache.data[ms.resp.PolicyResponse.Policy] = policyStatus + ms.s.cache.mutex.Unlock() +} diff --git a/pkg/policyStatus/some_test.go b/pkg/policyStatus/some_test.go new file mode 100644 index 0000000000..826c2c6eaa --- /dev/null +++ b/pkg/policyStatus/some_test.go @@ -0,0 +1,7 @@ +package policyStatus + +import "testing" + +func TestNewSync(t *testing.T) { + +} diff --git a/pkg/policyStatus/validateStats.go b/pkg/policyStatus/validateStats.go new file mode 100644 index 0000000000..96e204cac9 --- /dev/null +++ b/pkg/policyStatus/validateStats.go @@ -0,0 +1,88 @@ +package policyStatus + +import ( + "reflect" + "sort" + "time" + + v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + "github.com/nirmata/kyverno/pkg/engine/response" +) + +type validateStats struct { + s *Sync + resp response.EngineResponse +} + +func (s *Sync) UpdateStatusWithValidateStats(resp response.EngineResponse) { + s.listener <- &validateStats{ + s: s, + resp: resp, + } +} + +func (vs *validateStats) updateStatus() { + if reflect.DeepEqual(response.EngineResponse{}, vs.resp) { + return + } + + vs.s.cache.mutex.Lock() + var policyStatus v1.PolicyStatus + policyStatus, exist := vs.s.cache.data[vs.resp.PolicyResponse.Policy] + if !exist { + policy, _ := vs.s.policyStore.Get(vs.resp.PolicyResponse.Policy) + if policy != nil { + policyStatus = policy.Status + } + } + + var nameToRule = make(map[string]v1.RuleStats, 0) + for _, rule := range policyStatus.Rules { + nameToRule[rule.Name] = rule + } + + for _, rule := range vs.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 { + policyStatus.RulesAppliedCount++ + ruleStat.AppliedCount++ + } else { + policyStatus.RulesFailedCount++ + ruleStat.FailedCount++ + if vs.resp.PolicyResponse.ValidationFailureAction == "enforce" { + policyStatus.ResourcesBlockedCount++ + ruleStat.ResourcesBlockedCount++ + } + } + + 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 + }) + + policyStatus.AvgExecutionTime = policyAverageExecutionTime.String() + policyStatus.Rules = ruleStats + + vs.s.cache.data[vs.resp.PolicyResponse.Policy] = policyStatus + vs.s.cache.mutex.Unlock() +} diff --git a/pkg/policyStatus/violationCount.go b/pkg/policyStatus/violationCount.go new file mode 100644 index 0000000000..93f02fded1 --- /dev/null +++ b/pkg/policyStatus/violationCount.go @@ -0,0 +1,35 @@ +package policyStatus + +import v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + +type violationCount struct { + sync *Sync + policyName string + violatedRules []v1.ViolatedRule +} + +func (s *Sync) UpdatePolicyStatusWithViolationCount(policyName string, violatedRules []v1.ViolatedRule) { + s.listener <- &violationCount{ + sync: s, + policyName: policyName, + violatedRules: violatedRules, + } +} + +func (vc *violationCount) updateStatus() { + vc.sync.cache.mutex.Lock() + status := vc.sync.cache.data[vc.policyName] + + var ruleNameToViolations = make(map[string]int) + for _, rule := range vc.violatedRules { + ruleNameToViolations[rule.Name]++ + } + + for i := range status.Rules { + status.ViolationCount += ruleNameToViolations[status.Rules[i].Name] + status.Rules[i].ViolationCount += ruleNameToViolations[status.Rules[i].Name] + } + + vc.sync.cache.data[vc.policyName] = status + vc.sync.cache.mutex.Unlock() +} diff --git a/pkg/policyviolation/clusterpv.go b/pkg/policyviolation/clusterpv.go index 97409427c0..48d1ce4c30 100644 --- a/pkg/policyviolation/clusterpv.go +++ b/pkg/policyviolation/clusterpv.go @@ -4,13 +4,12 @@ import ( "fmt" "reflect" - "github.com/nirmata/kyverno/pkg/policy" - "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" kyvernov1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/kyverno/v1" kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1" client "github.com/nirmata/kyverno/pkg/dclient" + "github.com/nirmata/kyverno/pkg/policyStatus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -23,13 +22,13 @@ type clusterPV struct { // policy violation interface kyvernoInterface kyvernov1.KyvernoV1Interface // update policy stats with violationCount - policyStatus *policy.StatSync + policyStatus *policyStatus.Sync } func newClusterPV(dclient *client.Client, cpvLister kyvernolister.ClusterPolicyViolationLister, kyvernoInterface kyvernov1.KyvernoV1Interface, - policyStatus *policy.StatSync, + policyStatus *policyStatus.Sync, ) *clusterPV { cpv := clusterPV{ dclient: dclient, diff --git a/pkg/policyviolation/generator.go b/pkg/policyviolation/generator.go index f2309aa82e..22b538aeef 100644 --- a/pkg/policyviolation/generator.go +++ b/pkg/policyviolation/generator.go @@ -8,14 +8,13 @@ import ( "sync" "time" - "github.com/nirmata/kyverno/pkg/policy" - "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" kyvernov1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/kyverno/v1" kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1" kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1" + "github.com/nirmata/kyverno/pkg/policyStatus" dclient "github.com/nirmata/kyverno/pkg/dclient" unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -42,7 +41,7 @@ type Generator struct { nspvSynced cache.InformerSynced queue workqueue.RateLimitingInterface dataStore *dataStore - policyStatus *policy.StatSync + policyStatus *policyStatus.Sync } //NewDataStore returns an instance of data store @@ -107,7 +106,7 @@ func NewPVGenerator(client *kyvernoclient.Clientset, dclient *dclient.Client, pvInformer kyvernoinformer.ClusterPolicyViolationInformer, nspvInformer kyvernoinformer.PolicyViolationInformer, - policyStatus *policy.StatSync) *Generator { + policyStatus *policyStatus.Sync) *Generator { gen := Generator{ kyvernoInterface: client.KyvernoV1(), dclient: dclient, diff --git a/pkg/policyviolation/namespacedpv.go b/pkg/policyviolation/namespacedpv.go index 4dc3cad759..8c24d62a83 100644 --- a/pkg/policyviolation/namespacedpv.go +++ b/pkg/policyviolation/namespacedpv.go @@ -4,13 +4,12 @@ import ( "fmt" "reflect" - "github.com/nirmata/kyverno/pkg/policy" - "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" kyvernov1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/kyverno/v1" kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1" client "github.com/nirmata/kyverno/pkg/dclient" + "github.com/nirmata/kyverno/pkg/policyStatus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -23,13 +22,13 @@ type namespacedPV struct { // policy violation interface kyvernoInterface kyvernov1.KyvernoV1Interface // update policy status with violationCount - policyStatus *policy.StatSync + policyStatus *policyStatus.Sync } func newNamespacedPV(dclient *client.Client, nspvLister kyvernolister.PolicyViolationLister, kyvernoInterface kyvernov1.KyvernoV1Interface, - policyStatus *policy.StatSync, + policyStatus *policyStatus.Sync, ) *namespacedPV { nspv := namespacedPV{ dclient: dclient, diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index 6e51fd692b..e7733e5f72 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -10,8 +10,6 @@ import ( "net/http" "time" - "github.com/nirmata/kyverno/pkg/policy" - "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/checker" kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" @@ -20,6 +18,7 @@ import ( "github.com/nirmata/kyverno/pkg/config" client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/event" + "github.com/nirmata/kyverno/pkg/policyStatus" "github.com/nirmata/kyverno/pkg/policystore" "github.com/nirmata/kyverno/pkg/policyviolation" tlsutils "github.com/nirmata/kyverno/pkg/tls" @@ -56,7 +55,7 @@ type WebhookServer struct { // webhook registration client webhookRegistrationClient *webhookconfig.WebhookRegistrationClient // API to send policy stats for aggregation - status *policy.StatSync + status *policyStatus.Sync // helpers to validate against current loaded configuration configHandler config.Interface // channel for cleanup notification @@ -83,7 +82,7 @@ func NewWebhookServer( crbInformer rbacinformer.ClusterRoleBindingInformer, eventGen event.Interface, webhookRegistrationClient *webhookconfig.WebhookRegistrationClient, - statusSync *policy.StatSync, + statusSync *policyStatus.Sync, configHandler config.Interface, pMetaStore policystore.LookupInterface, pvGenerator policyviolation.GeneratorInterface,