mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
527 decoupling sender and reciever
This commit is contained in:
parent
053ccde6b8
commit
40e92ebacf
19 changed files with 652 additions and 562 deletions
|
@ -140,7 +140,6 @@ func main() {
|
||||||
// Policy Status Handler - deals with all logic related to policy status
|
// Policy Status Handler - deals with all logic related to policy status
|
||||||
statusSync := policyStatus.NewSync(
|
statusSync := policyStatus.NewSync(
|
||||||
pclient,
|
pclient,
|
||||||
stopCh,
|
|
||||||
policyMetaStore)
|
policyMetaStore)
|
||||||
|
|
||||||
// POLICY VIOLATION GENERATOR
|
// POLICY VIOLATION GENERATOR
|
||||||
|
|
58
pkg/generate/policyStatus_test.go
Normal file
58
pkg/generate/policyStatus_test.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package generate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
"github.com/nirmata/kyverno/pkg/policyStatus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dummyStore struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dummyStore) Get(policyName string) (*v1.ClusterPolicy, error) {
|
||||||
|
return &v1.ClusterPolicy{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Stats(t *testing.T) {
|
||||||
|
testCase := struct {
|
||||||
|
generatedCountStats []v1.GenerateRequest
|
||||||
|
expectedOutput []byte
|
||||||
|
}{
|
||||||
|
expectedOutput: []byte(`{"policy1":{"averageExecutionTime":"","resourcesGeneratedCount":1},"policy2":{"averageExecutionTime":"","resourcesGeneratedCount":1}}`),
|
||||||
|
generatedCountStats: []v1.GenerateRequest{
|
||||||
|
{
|
||||||
|
Spec: v1.GenerateRequestSpec{
|
||||||
|
Policy: "policy1",
|
||||||
|
},
|
||||||
|
Status: v1.GenerateRequestStatus{
|
||||||
|
GeneratedResources: make([]v1.ResourceSpec, 1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Spec: v1.GenerateRequestSpec{
|
||||||
|
Policy: "policy2",
|
||||||
|
},
|
||||||
|
Status: v1.GenerateRequestStatus{
|
||||||
|
GeneratedResources: make([]v1.ResourceSpec, 1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
s := policyStatus.NewSync(nil, &dummyStore{})
|
||||||
|
|
||||||
|
for _, generateCountStat := range testCase.generatedCountStats {
|
||||||
|
receiver := &generatedResourceCount{
|
||||||
|
generateRequest: generateCountStat,
|
||||||
|
}
|
||||||
|
receiver.UpdateStatus(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
output, _ := json.Marshal(s.Cache.Data)
|
||||||
|
if !reflect.DeepEqual(output, testCase.expectedOutput) {
|
||||||
|
t.Errorf("\n\nTestcase has failed\nExpected:\n%v\nGot:\n%v\n\n", string(testCase.expectedOutput), string(output))
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,7 +44,9 @@ func (sc StatusControl) Success(gr kyverno.GenerateRequest, genResources []kyver
|
||||||
gr.Status.GeneratedResources = genResources
|
gr.Status.GeneratedResources = genResources
|
||||||
|
|
||||||
if oldState != kyverno.Completed {
|
if oldState != kyverno.Completed {
|
||||||
go sc.policyStatus.UpdatePolicyStatusWithGeneratedResourceCount(gr)
|
go func() {
|
||||||
|
sc.policyStatus.Listener <- updatePolicyStatusWithGeneratedResourceCount(gr)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := sc.client.KyvernoV1().GenerateRequests("kyverno").UpdateStatus(&gr)
|
_, err := sc.client.KyvernoV1().GenerateRequests("kyverno").UpdateStatus(&gr)
|
||||||
|
@ -55,3 +57,29 @@ func (sc StatusControl) Success(gr kyverno.GenerateRequest, genResources []kyver
|
||||||
glog.V(4).Infof("updated gr %s status to %s", gr.Name, string(kyverno.Completed))
|
glog.V(4).Infof("updated gr %s status to %s", gr.Name, string(kyverno.Completed))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type generatedResourceCount struct {
|
||||||
|
generateRequest kyverno.GenerateRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
func updatePolicyStatusWithGeneratedResourceCount(generateRequest kyverno.GenerateRequest) *generatedResourceCount {
|
||||||
|
return &generatedResourceCount{
|
||||||
|
generateRequest: generateRequest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vc *generatedResourceCount) UpdateStatus(s *policyStatus.Sync) {
|
||||||
|
s.Cache.Mutex.Lock()
|
||||||
|
status, exist := s.Cache.Data[vc.generateRequest.Spec.Policy]
|
||||||
|
if !exist {
|
||||||
|
policy, _ := s.PolicyStore.Get(vc.generateRequest.Spec.Policy)
|
||||||
|
if policy != nil {
|
||||||
|
status = policy.Status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status.ResourcesGeneratedCount += len(vc.generateRequest.Status.GeneratedResources)
|
||||||
|
|
||||||
|
s.Cache.Data[vc.generateRequest.Spec.Policy] = status
|
||||||
|
s.Cache.Mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
package policyStatus
|
|
||||||
|
|
||||||
type statusUpdater interface {
|
|
||||||
updateStatus()
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
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()
|
|
||||||
policyStatus, exist := gs.s.cache.data[gs.resp.PolicyResponse.Policy]
|
|
||||||
if !exist {
|
|
||||||
if gs.s.policyStore != nil {
|
|
||||||
policy, _ := gs.s.policyStore.Get(gs.resp.PolicyResponse.Policy)
|
|
||||||
if policy != nil {
|
|
||||||
policyStatus = policy.Status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var nameToRule = make(map[string]v1.RuleStats)
|
|
||||||
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()
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
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, exist := vc.sync.cache.data[vc.generateRequest.Spec.Policy]
|
|
||||||
if !exist {
|
|
||||||
policy, _ := vc.sync.policyStore.Get(vc.generateRequest.Spec.Policy)
|
|
||||||
if policy != nil {
|
|
||||||
status = policy.Status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
status.ResourcesGeneratedCount += len(vc.generateRequest.Status.GeneratedResources)
|
|
||||||
|
|
||||||
vc.sync.cache.data[vc.generateRequest.Spec.Policy] = status
|
|
||||||
vc.sync.cache.mutex.Unlock()
|
|
||||||
}
|
|
|
@ -6,8 +6,6 @@ import (
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
|
||||||
"github.com/nirmata/kyverno/pkg/policystore"
|
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
|
||||||
"github.com/nirmata/kyverno/pkg/client/clientset/versioned"
|
"github.com/nirmata/kyverno/pkg/client/clientset/versioned"
|
||||||
|
@ -15,27 +13,35 @@ import (
|
||||||
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type statusUpdater interface {
|
||||||
|
UpdateStatus(s *Sync)
|
||||||
|
}
|
||||||
|
|
||||||
|
type policyStore interface {
|
||||||
|
Get(policyName string) (*v1.ClusterPolicy, error)
|
||||||
|
}
|
||||||
|
|
||||||
type Sync struct {
|
type Sync struct {
|
||||||
cache *cache
|
Cache *cache
|
||||||
listener chan statusUpdater
|
Listener chan statusUpdater
|
||||||
client *versioned.Clientset
|
client *versioned.Clientset
|
||||||
policyStore *policystore.PolicyStore
|
PolicyStore policyStore
|
||||||
}
|
}
|
||||||
|
|
||||||
type cache struct {
|
type cache struct {
|
||||||
mutex sync.RWMutex
|
Mutex sync.RWMutex
|
||||||
data map[string]v1.PolicyStatus
|
Data map[string]v1.PolicyStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSync(c *versioned.Clientset, pms *policystore.PolicyStore) *Sync {
|
func NewSync(c *versioned.Clientset, p policyStore) *Sync {
|
||||||
return &Sync{
|
return &Sync{
|
||||||
cache: &cache{
|
Cache: &cache{
|
||||||
mutex: sync.RWMutex{},
|
Mutex: sync.RWMutex{},
|
||||||
data: make(map[string]v1.PolicyStatus),
|
Data: make(map[string]v1.PolicyStatus),
|
||||||
},
|
},
|
||||||
client: c,
|
client: c,
|
||||||
policyStore: pms,
|
PolicyStore: p,
|
||||||
listener: make(chan statusUpdater),
|
Listener: make(chan statusUpdater),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,8 +58,8 @@ func (s *Sync) Run(workers int, stopCh <-chan struct{}) {
|
||||||
func (s *Sync) updateStatusCache(stopCh <-chan struct{}) {
|
func (s *Sync) updateStatusCache(stopCh <-chan struct{}) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case statusUpdater := <-s.listener:
|
case statusUpdater := <-s.Listener:
|
||||||
statusUpdater.updateStatus()
|
statusUpdater.UpdateStatus(s)
|
||||||
case <-stopCh:
|
case <-stopCh:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -61,24 +67,24 @@ func (s *Sync) updateStatusCache(stopCh <-chan struct{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sync) updatePolicyStatus() {
|
func (s *Sync) updatePolicyStatus() {
|
||||||
s.cache.mutex.Lock()
|
s.Cache.Mutex.Lock()
|
||||||
var nameToStatus = make(map[string]v1.PolicyStatus, len(s.cache.data))
|
var nameToStatus = make(map[string]v1.PolicyStatus, len(s.Cache.Data))
|
||||||
for k, v := range s.cache.data {
|
for k, v := range s.Cache.Data {
|
||||||
nameToStatus[k] = v
|
nameToStatus[k] = v
|
||||||
}
|
}
|
||||||
s.cache.mutex.Unlock()
|
s.Cache.Mutex.Unlock()
|
||||||
|
|
||||||
for policyName, status := range nameToStatus {
|
for policyName, status := range nameToStatus {
|
||||||
policy, err := s.policyStore.Get(policyName)
|
policy, err := s.PolicyStore.Get(policyName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
policy.Status = status
|
policy.Status = status
|
||||||
_, err = s.client.KyvernoV1().ClusterPolicies().UpdateStatus(policy)
|
_, err = s.client.KyvernoV1().ClusterPolicies().UpdateStatus(policy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.cache.mutex.Lock()
|
s.Cache.Mutex.Lock()
|
||||||
delete(s.cache.data, policyName)
|
delete(s.Cache.Data, policyName)
|
||||||
s.cache.mutex.Unlock()
|
s.Cache.Mutex.Unlock()
|
||||||
glog.V(4).Info(err)
|
glog.V(4).Info(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,88 +0,0 @@
|
||||||
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()
|
|
||||||
policyStatus, exist := ms.s.cache.data[ms.resp.PolicyResponse.Policy]
|
|
||||||
if !exist {
|
|
||||||
if ms.s.policyStore != nil {
|
|
||||||
policy, _ := ms.s.policyStore.Get(ms.resp.PolicyResponse.Policy)
|
|
||||||
if policy != nil {
|
|
||||||
policyStatus = policy.Status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var nameToRule = make(map[string]v1.RuleStats)
|
|
||||||
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()
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
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()
|
|
||||||
policyStatus, exist := vs.s.cache.data[vs.resp.PolicyResponse.Policy]
|
|
||||||
if !exist {
|
|
||||||
if vs.s.policyStore != nil {
|
|
||||||
policy, _ := vs.s.policyStore.Get(vs.resp.PolicyResponse.Policy)
|
|
||||||
if policy != nil {
|
|
||||||
policyStatus = policy.Status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var nameToRule = make(map[string]v1.RuleStats)
|
|
||||||
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()
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
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, exist := vc.sync.cache.data[vc.policyName]
|
|
||||||
if !exist {
|
|
||||||
policy, _ := vc.sync.policyStore.Get(vc.policyName)
|
|
||||||
if policy != nil {
|
|
||||||
status = policy.Status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
|
@ -100,7 +100,9 @@ func (cpv *clusterPV) createPV(newPv *kyverno.ClusterPolicyViolation) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if newPv.Annotations["fromSync"] != "true" {
|
if newPv.Annotations["fromSync"] != "true" {
|
||||||
go cpv.policyStatus.UpdatePolicyStatusWithViolationCount(newPv.Spec.Policy, newPv.Spec.ViolatedRules)
|
go func() {
|
||||||
|
cpv.policyStatus.Listener <- updatePolicyStatusWithViolationCount(newPv.Spec.Policy, newPv.Spec.ViolatedRules)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.Infof("policy violation created for resource %v", newPv.Spec.ResourceSpec)
|
glog.Infof("policy violation created for resource %v", newPv.Spec.ResourceSpec)
|
||||||
|
@ -126,7 +128,9 @@ func (cpv *clusterPV) updatePV(newPv, oldPv *kyverno.ClusterPolicyViolation) err
|
||||||
glog.Infof("cluster policy violation updated for resource %v", newPv.Spec.ResourceSpec)
|
glog.Infof("cluster policy violation updated for resource %v", newPv.Spec.ResourceSpec)
|
||||||
|
|
||||||
if newPv.Annotations["fromSync"] != "true" {
|
if newPv.Annotations["fromSync"] != "true" {
|
||||||
go cpv.policyStatus.UpdatePolicyStatusWithViolationCount(newPv.Spec.Policy, newPv.Spec.ViolatedRules)
|
go func() {
|
||||||
|
cpv.policyStatus.Listener <- updatePolicyStatusWithViolationCount(newPv.Spec.Policy, newPv.Spec.ViolatedRules)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,12 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/nirmata/kyverno/pkg/policyStatus"
|
||||||
|
|
||||||
backoff "github.com/cenkalti/backoff"
|
backoff "github.com/cenkalti/backoff"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
@ -70,3 +73,39 @@ func converLabelToSelector(labelMap map[string]string) (labels.Selector, error)
|
||||||
|
|
||||||
return policyViolationSelector, nil
|
return policyViolationSelector, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type violationCount struct {
|
||||||
|
policyName string
|
||||||
|
violatedRules []v1.ViolatedRule
|
||||||
|
}
|
||||||
|
|
||||||
|
func updatePolicyStatusWithViolationCount(policyName string, violatedRules []kyverno.ViolatedRule) *violationCount {
|
||||||
|
return &violationCount{
|
||||||
|
policyName: policyName,
|
||||||
|
violatedRules: violatedRules,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vc *violationCount) UpdateStatus(s *policyStatus.Sync) {
|
||||||
|
s.Cache.Mutex.Lock()
|
||||||
|
status, exist := s.Cache.Data[vc.policyName]
|
||||||
|
if !exist {
|
||||||
|
policy, _ := s.PolicyStore.Get(vc.policyName)
|
||||||
|
if policy != nil {
|
||||||
|
status = policy.Status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Cache.Data[vc.policyName] = status
|
||||||
|
s.Cache.Mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
|
@ -99,7 +99,9 @@ func (nspv *namespacedPV) createPV(newPv *kyverno.PolicyViolation) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if newPv.Annotations["fromSync"] != "true" {
|
if newPv.Annotations["fromSync"] != "true" {
|
||||||
go nspv.policyStatus.UpdatePolicyStatusWithViolationCount(newPv.Spec.Policy, newPv.Spec.ViolatedRules)
|
go func() {
|
||||||
|
nspv.policyStatus.Listener <- updatePolicyStatusWithViolationCount(newPv.Spec.Policy, newPv.Spec.ViolatedRules)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
glog.Infof("policy violation created for resource %v", newPv.Spec.ResourceSpec)
|
glog.Infof("policy violation created for resource %v", newPv.Spec.ResourceSpec)
|
||||||
return nil
|
return nil
|
||||||
|
@ -122,7 +124,9 @@ func (nspv *namespacedPV) updatePV(newPv, oldPv *kyverno.PolicyViolation) error
|
||||||
}
|
}
|
||||||
|
|
||||||
if newPv.Annotations["fromSync"] != "true" {
|
if newPv.Annotations["fromSync"] != "true" {
|
||||||
go nspv.policyStatus.UpdatePolicyStatusWithViolationCount(newPv.Spec.Policy, newPv.Spec.ViolatedRules)
|
go func() {
|
||||||
|
nspv.policyStatus.Listener <- updatePolicyStatusWithViolationCount(newPv.Spec.Policy, newPv.Spec.ViolatedRules)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
glog.Infof("namespaced policy violation updated for resource %v", newPv.Spec.ResourceSpec)
|
glog.Infof("namespaced policy violation updated for resource %v", newPv.Spec.ResourceSpec)
|
||||||
return nil
|
return nil
|
||||||
|
|
74
pkg/policyviolation/policyStatus_test.go
Normal file
74
pkg/policyviolation/policyStatus_test.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package policyviolation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nirmata/kyverno/pkg/policyStatus"
|
||||||
|
|
||||||
|
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dummyStore struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dummyStore) Get(policyName string) (*v1.ClusterPolicy, error) {
|
||||||
|
return &v1.ClusterPolicy{
|
||||||
|
Status: v1.PolicyStatus{
|
||||||
|
Rules: []v1.RuleStats{
|
||||||
|
{
|
||||||
|
Name: "rule4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Stats(t *testing.T) {
|
||||||
|
testCase := struct {
|
||||||
|
violationCountStats []struct {
|
||||||
|
policyName string
|
||||||
|
violatedRules []v1.ViolatedRule
|
||||||
|
}
|
||||||
|
expectedOutput []byte
|
||||||
|
}{
|
||||||
|
expectedOutput: []byte(`{"policy1":{"averageExecutionTime":"","violationCount":1,"ruleStatus":[{"ruleName":"rule4","violationCount":1}]},"policy2":{"averageExecutionTime":"","violationCount":1,"ruleStatus":[{"ruleName":"rule4","violationCount":1}]}}`),
|
||||||
|
violationCountStats: []struct {
|
||||||
|
policyName string
|
||||||
|
violatedRules []v1.ViolatedRule
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
policyName: "policy1",
|
||||||
|
violatedRules: []v1.ViolatedRule{
|
||||||
|
{
|
||||||
|
Name: "rule4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
policyName: "policy2",
|
||||||
|
violatedRules: []v1.ViolatedRule{
|
||||||
|
{
|
||||||
|
Name: "rule4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
s := policyStatus.NewSync(nil, &dummyStore{})
|
||||||
|
|
||||||
|
for _, violationCountStat := range testCase.violationCountStats {
|
||||||
|
receiver := &violationCount{
|
||||||
|
policyName: violationCountStat.policyName,
|
||||||
|
violatedRules: violationCountStat.violatedRules,
|
||||||
|
}
|
||||||
|
receiver.UpdateStatus(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
output, _ := json.Marshal(s.Cache.Data)
|
||||||
|
if !reflect.DeepEqual(output, testCase.expectedOutput) {
|
||||||
|
t.Errorf("\n\nTestcase has failed\nExpected:\n%v\nGot:\n%v\n\n", string(testCase.expectedOutput), string(output))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,15 @@
|
||||||
package webhooks
|
package webhooks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/nirmata/kyverno/pkg/policyStatus"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
"github.com/nirmata/kyverno/pkg/engine"
|
"github.com/nirmata/kyverno/pkg/engine"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/context"
|
"github.com/nirmata/kyverno/pkg/engine/context"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/response"
|
"github.com/nirmata/kyverno/pkg/engine/response"
|
||||||
|
@ -61,7 +68,9 @@ func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, polic
|
||||||
if len(engineResponse.PolicyResponse.Rules) > 0 {
|
if len(engineResponse.PolicyResponse.Rules) > 0 {
|
||||||
// some generate rules do apply to the resource
|
// some generate rules do apply to the resource
|
||||||
engineResponses = append(engineResponses, engineResponse)
|
engineResponses = append(engineResponses, engineResponse)
|
||||||
go ws.status.UpdateStatusWithGenerateStats(engineResponse)
|
go func() {
|
||||||
|
ws.status.Listener <- updateStatusWithGenerateStats(engineResponse)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Adds Generate Request to a channel(queue size 1000) to generators
|
// Adds Generate Request to a channel(queue size 1000) to generators
|
||||||
|
@ -103,3 +112,87 @@ func transform(userRequestInfo kyverno.RequestInfo, er response.EngineResponse)
|
||||||
}
|
}
|
||||||
return gr
|
return gr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type generateStats struct {
|
||||||
|
resp response.EngineResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateStatusWithGenerateStats(resp response.EngineResponse) *generateStats {
|
||||||
|
return &generateStats{
|
||||||
|
resp: resp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gs *generateStats) UpdateStatus(s *policyStatus.Sync) {
|
||||||
|
if reflect.DeepEqual(response.EngineResponse{}, gs.resp) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Cache.Mutex.Lock()
|
||||||
|
status, exist := s.Cache.Data[gs.resp.PolicyResponse.Policy]
|
||||||
|
if !exist {
|
||||||
|
if s.PolicyStore != nil {
|
||||||
|
policy, _ := s.PolicyStore.Get(gs.resp.PolicyResponse.Policy)
|
||||||
|
if policy != nil {
|
||||||
|
status = policy.Status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
s.Cache.Data[gs.resp.PolicyResponse.Policy] = status
|
||||||
|
s.Cache.Mutex.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
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
package webhooks
|
package webhooks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/nirmata/kyverno/pkg/policyStatus"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
"github.com/nirmata/kyverno/pkg/engine"
|
"github.com/nirmata/kyverno/pkg/engine"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/context"
|
"github.com/nirmata/kyverno/pkg/engine/context"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/response"
|
"github.com/nirmata/kyverno/pkg/engine/response"
|
||||||
|
@ -58,7 +63,9 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
|
||||||
policyContext.Policy = policy
|
policyContext.Policy = policy
|
||||||
engineResponse := engine.Mutate(policyContext)
|
engineResponse := engine.Mutate(policyContext)
|
||||||
engineResponses = append(engineResponses, engineResponse)
|
engineResponses = append(engineResponses, engineResponse)
|
||||||
go ws.status.UpdateStatusWithMutateStats(engineResponse)
|
go func() {
|
||||||
|
ws.status.Listener <- updateStatusWithMutateStats(engineResponse)
|
||||||
|
}()
|
||||||
if !engineResponse.IsSuccesful() {
|
if !engineResponse.IsSuccesful() {
|
||||||
glog.V(4).Infof("Failed to apply policy %s on resource %s/%s\n", policy.Name, resource.GetNamespace(), resource.GetName())
|
glog.V(4).Infof("Failed to apply policy %s on resource %s/%s\n", policy.Name, resource.GetNamespace(), resource.GetName())
|
||||||
continue
|
continue
|
||||||
|
@ -106,3 +113,79 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
|
||||||
// patches holds all the successful patches, if no patch is created, it returns nil
|
// patches holds all the successful patches, if no patch is created, it returns nil
|
||||||
return engineutils.JoinPatches(patches)
|
return engineutils.JoinPatches(patches)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mutateStats struct {
|
||||||
|
resp response.EngineResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateStatusWithMutateStats(resp response.EngineResponse) *mutateStats {
|
||||||
|
return &mutateStats{
|
||||||
|
resp: resp,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *mutateStats) UpdateStatus(s *policyStatus.Sync) {
|
||||||
|
if reflect.DeepEqual(response.EngineResponse{}, ms.resp) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Cache.Mutex.Lock()
|
||||||
|
status, exist := s.Cache.Data[ms.resp.PolicyResponse.Policy]
|
||||||
|
if !exist {
|
||||||
|
if s.PolicyStore != nil {
|
||||||
|
policy, _ := s.PolicyStore.Get(ms.resp.PolicyResponse.Policy)
|
||||||
|
if policy != nil {
|
||||||
|
status = policy.Status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
s.Cache.Data[ms.resp.PolicyResponse.Policy] = status
|
||||||
|
s.Cache.Mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package policyStatus
|
package webhooks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -7,151 +7,23 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
|
||||||
"github.com/nirmata/kyverno/pkg/engine/response"
|
"github.com/nirmata/kyverno/pkg/engine/response"
|
||||||
|
"github.com/nirmata/kyverno/pkg/policyStatus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Stats(t *testing.T) {
|
type dummyStore struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dummyStore) Get(policyName string) (*v1.ClusterPolicy, error) {
|
||||||
|
return &v1.ClusterPolicy{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_GenerateStats(t *testing.T) {
|
||||||
testCase := struct {
|
testCase := struct {
|
||||||
mutateStats []response.EngineResponse
|
generateStats []response.EngineResponse
|
||||||
validateStats []response.EngineResponse
|
expectedOutput []byte
|
||||||
generateStats []response.EngineResponse
|
|
||||||
violationCountStats []struct {
|
|
||||||
policyName string
|
|
||||||
violatedRules []v1.ViolatedRule
|
|
||||||
}
|
|
||||||
generatedCountStats []v1.GenerateRequest
|
|
||||||
expectedOutput []byte
|
|
||||||
}{
|
}{
|
||||||
expectedOutput: []byte(`{"policy1":{"averageExecutionTime":"1.482µs","violationCount":1,"rulesFailedCount":3,"rulesAppliedCount":3,"resourcesBlockedCount":1,"resourcesMutatedCount":1,"resourcesGeneratedCount":1,"ruleStatus":[{"ruleName":"rule1","averageExecutionTime":"243ns","appliedCount":1,"resourcesMutatedCount":1},{"ruleName":"rule2","averageExecutionTime":"251ns","failedCount":1},{"ruleName":"rule3","averageExecutionTime":"243ns","appliedCount":1},{"ruleName":"rule4","averageExecutionTime":"251ns","violationCount":1,"failedCount":1,"resourcesBlockedCount":1},{"ruleName":"rule5","averageExecutionTime":"243ns","appliedCount":1},{"ruleName":"rule6","averageExecutionTime":"251ns","failedCount":1}]},"policy2":{"averageExecutionTime":"1.299µs","violationCount":1,"rulesFailedCount":3,"rulesAppliedCount":3,"resourcesMutatedCount":1,"resourcesGeneratedCount":1,"ruleStatus":[{"ruleName":"rule1","averageExecutionTime":"222ns","appliedCount":1,"resourcesMutatedCount":1},{"ruleName":"rule2","averageExecutionTime":"211ns","failedCount":1},{"ruleName":"rule3","averageExecutionTime":"222ns","appliedCount":1},{"ruleName":"rule4","averageExecutionTime":"211ns","violationCount":1,"failedCount":1},{"ruleName":"rule5","averageExecutionTime":"222ns","appliedCount":1},{"ruleName":"rule6","averageExecutionTime":"211ns","failedCount":1}]}}`),
|
expectedOutput: []byte(`{"policy1":{"averageExecutionTime":"494ns","rulesFailedCount":1,"rulesAppliedCount":1,"ruleStatus":[{"ruleName":"rule5","averageExecutionTime":"243ns","appliedCount":1},{"ruleName":"rule6","averageExecutionTime":"251ns","failedCount":1}]},"policy2":{"averageExecutionTime":"433ns","rulesFailedCount":1,"rulesAppliedCount":1,"ruleStatus":[{"ruleName":"rule5","averageExecutionTime":"222ns","appliedCount":1},{"ruleName":"rule6","averageExecutionTime":"211ns","failedCount":1}]}}`),
|
||||||
generatedCountStats: []v1.GenerateRequest{
|
|
||||||
{
|
|
||||||
Spec: v1.GenerateRequestSpec{
|
|
||||||
Policy: "policy1",
|
|
||||||
},
|
|
||||||
Status: v1.GenerateRequestStatus{
|
|
||||||
GeneratedResources: make([]v1.ResourceSpec, 1),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Spec: v1.GenerateRequestSpec{
|
|
||||||
Policy: "policy2",
|
|
||||||
},
|
|
||||||
Status: v1.GenerateRequestStatus{
|
|
||||||
GeneratedResources: make([]v1.ResourceSpec, 1),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
violationCountStats: []struct {
|
|
||||||
policyName string
|
|
||||||
violatedRules []v1.ViolatedRule
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
policyName: "policy1",
|
|
||||||
violatedRules: []v1.ViolatedRule{
|
|
||||||
{
|
|
||||||
Name: "rule4",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
policyName: "policy2",
|
|
||||||
violatedRules: []v1.ViolatedRule{
|
|
||||||
{
|
|
||||||
Name: "rule4",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mutateStats: []response.EngineResponse{
|
|
||||||
{
|
|
||||||
PolicyResponse: response.PolicyResponse{
|
|
||||||
Policy: "policy1",
|
|
||||||
Rules: []response.RuleResponse{
|
|
||||||
{
|
|
||||||
Name: "rule1",
|
|
||||||
Success: true,
|
|
||||||
RuleStats: response.RuleStats{
|
|
||||||
ProcessingTime: time.Nanosecond * 243,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "rule2",
|
|
||||||
Success: false,
|
|
||||||
RuleStats: response.RuleStats{
|
|
||||||
ProcessingTime: time.Nanosecond * 251,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PolicyResponse: response.PolicyResponse{
|
|
||||||
Policy: "policy2",
|
|
||||||
Rules: []response.RuleResponse{
|
|
||||||
{
|
|
||||||
Name: "rule1",
|
|
||||||
Success: true,
|
|
||||||
RuleStats: response.RuleStats{
|
|
||||||
ProcessingTime: time.Nanosecond * 222,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "rule2",
|
|
||||||
Success: false,
|
|
||||||
RuleStats: response.RuleStats{
|
|
||||||
ProcessingTime: time.Nanosecond * 211,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
validateStats: []response.EngineResponse{
|
|
||||||
{
|
|
||||||
PolicyResponse: response.PolicyResponse{
|
|
||||||
Policy: "policy1",
|
|
||||||
ValidationFailureAction: "enforce",
|
|
||||||
Rules: []response.RuleResponse{
|
|
||||||
{
|
|
||||||
Name: "rule3",
|
|
||||||
Success: true,
|
|
||||||
RuleStats: response.RuleStats{
|
|
||||||
ProcessingTime: time.Nanosecond * 243,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "rule4",
|
|
||||||
Success: false,
|
|
||||||
RuleStats: response.RuleStats{
|
|
||||||
ProcessingTime: time.Nanosecond * 251,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PolicyResponse: response.PolicyResponse{
|
|
||||||
Policy: "policy2",
|
|
||||||
Rules: []response.RuleResponse{
|
|
||||||
{
|
|
||||||
Name: "rule3",
|
|
||||||
Success: true,
|
|
||||||
RuleStats: response.RuleStats{
|
|
||||||
ProcessingTime: time.Nanosecond * 222,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "rule4",
|
|
||||||
Success: false,
|
|
||||||
RuleStats: response.RuleStats{
|
|
||||||
ProcessingTime: time.Nanosecond * 211,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
generateStats: []response.EngineResponse{
|
generateStats: []response.EngineResponse{
|
||||||
{
|
{
|
||||||
PolicyResponse: response.PolicyResponse{
|
PolicyResponse: response.PolicyResponse{
|
||||||
|
@ -198,49 +70,149 @@ func Test_Stats(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s := NewSync(nil, nil, nil)
|
s := policyStatus.NewSync(nil, &dummyStore{})
|
||||||
for _, mutateStat := range testCase.mutateStats {
|
|
||||||
receiver := &mutateStats{
|
|
||||||
s: s,
|
|
||||||
resp: mutateStat,
|
|
||||||
}
|
|
||||||
receiver.updateStatus()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, validateStat := range testCase.validateStats {
|
|
||||||
receiver := &validateStats{
|
|
||||||
s: s,
|
|
||||||
resp: validateStat,
|
|
||||||
}
|
|
||||||
receiver.updateStatus()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, generateStat := range testCase.generateStats {
|
for _, generateStat := range testCase.generateStats {
|
||||||
receiver := &generateStats{
|
receiver := &generateStats{
|
||||||
s: s,
|
|
||||||
resp: generateStat,
|
resp: generateStat,
|
||||||
}
|
}
|
||||||
receiver.updateStatus()
|
receiver.UpdateStatus(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, generateCountStat := range testCase.generatedCountStats {
|
output, _ := json.Marshal(s.Cache.Data)
|
||||||
receiver := &generatedResourceCount{
|
if !reflect.DeepEqual(output, testCase.expectedOutput) {
|
||||||
sync: s,
|
t.Errorf("\n\nTestcase has failed\nExpected:\n%v\nGot:\n%v\n\n", string(testCase.expectedOutput), string(output))
|
||||||
generateRequest: generateCountStat,
|
}
|
||||||
}
|
}
|
||||||
receiver.updateStatus()
|
|
||||||
}
|
func Test_MutateStats(t *testing.T) {
|
||||||
|
testCase := struct {
|
||||||
for _, violationCountStat := range testCase.violationCountStats {
|
mutateStats []response.EngineResponse
|
||||||
receiver := &violationCount{
|
expectedOutput []byte
|
||||||
sync: s,
|
}{
|
||||||
policyName: violationCountStat.policyName,
|
expectedOutput: []byte(`{"policy1":{"averageExecutionTime":"494ns","rulesFailedCount":1,"rulesAppliedCount":1,"resourcesMutatedCount":1,"ruleStatus":[{"ruleName":"rule1","averageExecutionTime":"243ns","appliedCount":1,"resourcesMutatedCount":1},{"ruleName":"rule2","averageExecutionTime":"251ns","failedCount":1}]},"policy2":{"averageExecutionTime":"433ns","rulesFailedCount":1,"rulesAppliedCount":1,"resourcesMutatedCount":1,"ruleStatus":[{"ruleName":"rule1","averageExecutionTime":"222ns","appliedCount":1,"resourcesMutatedCount":1},{"ruleName":"rule2","averageExecutionTime":"211ns","failedCount":1}]}}`),
|
||||||
violatedRules: violationCountStat.violatedRules,
|
mutateStats: []response.EngineResponse{
|
||||||
}
|
{
|
||||||
receiver.updateStatus()
|
PolicyResponse: response.PolicyResponse{
|
||||||
}
|
Policy: "policy1",
|
||||||
|
Rules: []response.RuleResponse{
|
||||||
output, _ := json.Marshal(s.cache.data)
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Success: true,
|
||||||
|
RuleStats: response.RuleStats{
|
||||||
|
ProcessingTime: time.Nanosecond * 243,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "rule2",
|
||||||
|
Success: false,
|
||||||
|
RuleStats: response.RuleStats{
|
||||||
|
ProcessingTime: time.Nanosecond * 251,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PolicyResponse: response.PolicyResponse{
|
||||||
|
Policy: "policy2",
|
||||||
|
Rules: []response.RuleResponse{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Success: true,
|
||||||
|
RuleStats: response.RuleStats{
|
||||||
|
ProcessingTime: time.Nanosecond * 222,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "rule2",
|
||||||
|
Success: false,
|
||||||
|
RuleStats: response.RuleStats{
|
||||||
|
ProcessingTime: time.Nanosecond * 211,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
s := policyStatus.NewSync(nil, &dummyStore{})
|
||||||
|
for _, mutateStat := range testCase.mutateStats {
|
||||||
|
receiver := &mutateStats{
|
||||||
|
resp: mutateStat,
|
||||||
|
}
|
||||||
|
receiver.UpdateStatus(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
output, _ := json.Marshal(s.Cache.Data)
|
||||||
|
if !reflect.DeepEqual(output, testCase.expectedOutput) {
|
||||||
|
t.Errorf("\n\nTestcase has failed\nExpected:\n%v\nGot:\n%v\n\n", string(testCase.expectedOutput), string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ValidateStats(t *testing.T) {
|
||||||
|
testCase := struct {
|
||||||
|
validateStats []response.EngineResponse
|
||||||
|
expectedOutput []byte
|
||||||
|
}{
|
||||||
|
expectedOutput: []byte(`{"policy1":{"averageExecutionTime":"494ns","rulesFailedCount":1,"rulesAppliedCount":1,"resourcesBlockedCount":1,"ruleStatus":[{"ruleName":"rule3","averageExecutionTime":"243ns","appliedCount":1},{"ruleName":"rule4","averageExecutionTime":"251ns","failedCount":1,"resourcesBlockedCount":1}]},"policy2":{"averageExecutionTime":"433ns","rulesFailedCount":1,"rulesAppliedCount":1,"ruleStatus":[{"ruleName":"rule3","averageExecutionTime":"222ns","appliedCount":1},{"ruleName":"rule4","averageExecutionTime":"211ns","failedCount":1}]}}`),
|
||||||
|
validateStats: []response.EngineResponse{
|
||||||
|
{
|
||||||
|
PolicyResponse: response.PolicyResponse{
|
||||||
|
Policy: "policy1",
|
||||||
|
ValidationFailureAction: "enforce",
|
||||||
|
Rules: []response.RuleResponse{
|
||||||
|
{
|
||||||
|
Name: "rule3",
|
||||||
|
Success: true,
|
||||||
|
RuleStats: response.RuleStats{
|
||||||
|
ProcessingTime: time.Nanosecond * 243,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "rule4",
|
||||||
|
Success: false,
|
||||||
|
RuleStats: response.RuleStats{
|
||||||
|
ProcessingTime: time.Nanosecond * 251,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PolicyResponse: response.PolicyResponse{
|
||||||
|
Policy: "policy2",
|
||||||
|
Rules: []response.RuleResponse{
|
||||||
|
{
|
||||||
|
Name: "rule3",
|
||||||
|
Success: true,
|
||||||
|
RuleStats: response.RuleStats{
|
||||||
|
ProcessingTime: time.Nanosecond * 222,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "rule4",
|
||||||
|
Success: false,
|
||||||
|
RuleStats: response.RuleStats{
|
||||||
|
ProcessingTime: time.Nanosecond * 211,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
s := policyStatus.NewSync(nil, &dummyStore{})
|
||||||
|
for _, validateStat := range testCase.validateStats {
|
||||||
|
receiver := &validateStats{
|
||||||
|
resp: validateStat,
|
||||||
|
}
|
||||||
|
receiver.UpdateStatus(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
output, _ := json.Marshal(s.Cache.Data)
|
||||||
if !reflect.DeepEqual(output, testCase.expectedOutput) {
|
if !reflect.DeepEqual(output, testCase.expectedOutput) {
|
||||||
t.Errorf("\n\nTestcase has failed\nExpected:\n%v\nGot:\n%v\n\n", string(testCase.expectedOutput), string(output))
|
t.Errorf("\n\nTestcase has failed\nExpected:\n%v\nGot:\n%v\n\n", string(testCase.expectedOutput), string(output))
|
||||||
}
|
}
|
|
@ -2,10 +2,14 @@ package webhooks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/nirmata/kyverno/pkg/policyStatus"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
"github.com/nirmata/kyverno/pkg/engine"
|
"github.com/nirmata/kyverno/pkg/engine"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/context"
|
"github.com/nirmata/kyverno/pkg/engine/context"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/response"
|
"github.com/nirmata/kyverno/pkg/engine/response"
|
||||||
|
@ -69,7 +73,9 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
engineResponses = append(engineResponses, engineResponse)
|
engineResponses = append(engineResponses, engineResponse)
|
||||||
go ws.status.UpdateStatusWithValidateStats(engineResponse)
|
go func() {
|
||||||
|
ws.status.Listener <- updateStatusWithValidateStats(engineResponse)
|
||||||
|
}()
|
||||||
if !engineResponse.IsSuccesful() {
|
if !engineResponse.IsSuccesful() {
|
||||||
glog.V(4).Infof("Failed to apply policy %s on resource %s/%s\n", policy.Name, newR.GetNamespace(), newR.GetName())
|
glog.V(4).Infof("Failed to apply policy %s on resource %s/%s\n", policy.Name, newR.GetNamespace(), newR.GetName())
|
||||||
continue
|
continue
|
||||||
|
@ -99,3 +105,80 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
|
||||||
glog.V(4).Infof("report: %v %s/%s/%s", time.Since(reportTime), request.Kind, request.Namespace, request.Name)
|
glog.V(4).Infof("report: %v %s/%s/%s", time.Since(reportTime), request.Kind, request.Namespace, request.Name)
|
||||||
return true, ""
|
return true, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type validateStats struct {
|
||||||
|
resp response.EngineResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateStatusWithValidateStats(resp response.EngineResponse) *validateStats {
|
||||||
|
return &validateStats{
|
||||||
|
resp: resp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vs *validateStats) UpdateStatus(s *policyStatus.Sync) {
|
||||||
|
if reflect.DeepEqual(response.EngineResponse{}, vs.resp) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Cache.Mutex.Lock()
|
||||||
|
status, exist := s.Cache.Data[vs.resp.PolicyResponse.Policy]
|
||||||
|
if !exist {
|
||||||
|
if s.PolicyStore != nil {
|
||||||
|
policy, _ := s.PolicyStore.Get(vs.resp.PolicyResponse.Policy)
|
||||||
|
if policy != nil {
|
||||||
|
status = policy.Status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var nameToRule = make(map[string]v1.RuleStats)
|
||||||
|
for _, rule := range status.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 {
|
||||||
|
status.RulesAppliedCount++
|
||||||
|
ruleStat.AppliedCount++
|
||||||
|
} else {
|
||||||
|
status.RulesFailedCount++
|
||||||
|
ruleStat.FailedCount++
|
||||||
|
if vs.resp.PolicyResponse.ValidationFailureAction == "enforce" {
|
||||||
|
status.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
|
||||||
|
})
|
||||||
|
|
||||||
|
status.AvgExecutionTime = policyAverageExecutionTime.String()
|
||||||
|
status.Rules = ruleStats
|
||||||
|
|
||||||
|
s.Cache.Data[vs.resp.PolicyResponse.Policy] = status
|
||||||
|
s.Cache.Mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue