From ef096441ac0b4c0a8e1490ae4df629fda326fc8b Mon Sep 17 00:00:00 2001 From: Ahmed El-Sayed Date: Mon, 10 Oct 2022 14:28:33 +0200 Subject: [PATCH 1/3] fix flux violations reference --- .../flux-notification/flux_notification.go | 3 +- internal/sink/flux-notification/utils_test.go | 30 ------- internal/sink/k8s-event/k8s_event.go | 14 ++++ internal/sink/k8s-event/k8s_event_test.go | 78 +++++++++++++++++-- .../flux-notification => utils}/utils.go | 5 +- internal/utils/utils_test.go | 30 +++++++ 6 files changed, 118 insertions(+), 42 deletions(-) delete mode 100644 internal/sink/flux-notification/utils_test.go rename internal/{sink/flux-notification => utils}/utils.go (83%) create mode 100644 internal/utils/utils_test.go diff --git a/internal/sink/flux-notification/flux_notification.go b/internal/sink/flux-notification/flux_notification.go index 62bcc5e6..398af4cc 100644 --- a/internal/sink/flux-notification/flux_notification.go +++ b/internal/sink/flux-notification/flux_notification.go @@ -6,6 +6,7 @@ import ( "github.com/MagalixTechnologies/core/logger" "github.com/MagalixTechnologies/policy-core/domain" + "github.com/weaveworks/policy-agent/internal/utils" "k8s.io/client-go/tools/record" ) @@ -65,7 +66,7 @@ func (f *FluxNotificationSink) writeWorker(ctx context.Context) error { } func (f *FluxNotificationSink) write(result domain.PolicyValidation) { - fluxObject := getFluxObject(result.Entity.Labels) + fluxObject := utils.GetFluxObject(result.Entity.Labels) if fluxObject == nil { logger.Debugw( fmt.Sprintf("discarding %s result for orphan entity", result.Type), diff --git a/internal/sink/flux-notification/utils_test.go b/internal/sink/flux-notification/utils_test.go deleted file mode 100644 index 8ac09bd8..00000000 --- a/internal/sink/flux-notification/utils_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package flux_notification - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" -) - -func TestGetFluxObject(t *testing.T) { - fluxObj := getFluxObject(map[string]string{}) - assert.Equal(t, fluxObj, nil) - - for apiVersion, kind := range fluxControllerKindMap { - fluxObj := getFluxObject(map[string]string{ - fmt.Sprintf("%s/name", apiVersion): "my-app", - fmt.Sprintf("%s/namespace", apiVersion): "default", - }) - - assert.NotEqual(t, fluxObj, nil) - - obj := fluxObj.(*unstructured.Unstructured) - - assert.Equal(t, obj.GetAPIVersion(), apiVersion) - assert.Equal(t, obj.GetKind(), kind) - assert.Equal(t, obj.GetNamespace(), "default") - assert.Equal(t, obj.GetName(), "my-app") - } -} diff --git a/internal/sink/k8s-event/k8s_event.go b/internal/sink/k8s-event/k8s_event.go index a45f06e9..93addd2e 100644 --- a/internal/sink/k8s-event/k8s_event.go +++ b/internal/sink/k8s-event/k8s_event.go @@ -6,6 +6,7 @@ import ( "github.com/MagalixTechnologies/core/logger" "github.com/MagalixTechnologies/policy-core/domain" + "github.com/weaveworks/policy-agent/internal/utils" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -78,6 +79,19 @@ func (f *K8sEventSink) writeWorker(ctx context.Context) error { func (k *K8sEventSink) write(ctx context.Context, result domain.PolicyValidation) { event, err := domain.NewK8sEventFromPolicyValidation(result) + + fluxObject := utils.GetFluxObject(result.Entity.Labels) + if fluxObject != nil { + event.InvolvedObject = v1.ObjectReference{ + UID: fluxObject.GetUID(), + APIVersion: fluxObject.GetAPIVersion(), + Kind: fluxObject.GetKind(), + Name: fluxObject.GetName(), + Namespace: fluxObject.GetNamespace(), + ResourceVersion: fluxObject.GetResourceVersion(), + } + } + if err != nil { logger.Errorw( "failed to create event from policy validation", diff --git a/internal/sink/k8s-event/k8s_event_test.go b/internal/sink/k8s-event/k8s_event_test.go index d2a8b5f8..1467a38d 100644 --- a/internal/sink/k8s-event/k8s_event_test.go +++ b/internal/sink/k8s-event/k8s_event_test.go @@ -57,13 +57,41 @@ func TestK8sEventSink(t *testing.T) { Labels: map[string]string{}, } + fluxHelmViolatingEntity := domain.Entity{ + ID: uuid.NewV4().String(), + APIVersion: "v1", + Kind: "Deployment", + Name: "my-helm-violating-entity", + Namespace: "default", + Manifest: map[string]interface{}{}, + ResourceVersion: "1", + Labels: map[string]string{ + "helm.toolkit.fluxcd.io/name": "my-helm-app-name", + "helm.toolkit.fluxcd.io/namespace": "my-helm-app-namespace", + }, + } + + fluxKustomizeViolatingEntity := domain.Entity{ + ID: uuid.NewV4().String(), + APIVersion: "v1", + Kind: "Deployment", + Name: "my-kustomize-violating-entity", + Namespace: "default", + Manifest: map[string]interface{}{}, + ResourceVersion: "1", + Labels: map[string]string{ + "kustomize.toolkit.fluxcd.io/name": "my-kustomize-app-name", + "kustomize.toolkit.fluxcd.io/namespace": "my-kustomize-app-namespace", + }, + } + results := []domain.PolicyValidation{ { ID: uuid.NewV4().String(), Policy: policy, Entity: violatingEntity, Status: domain.PolicyValidationStatusViolating, - Message: "message", + Message: "violating-entity", Type: "Admission", Trigger: "Admission", CreatedAt: time.Now(), @@ -73,7 +101,27 @@ func TestK8sEventSink(t *testing.T) { Policy: policy, Entity: compliantEntity, Status: domain.PolicyValidationStatusCompliant, - Message: "message", + Message: "compliant-entity", + Type: "Admission", + Trigger: "Admission", + CreatedAt: time.Now(), + }, + { + ID: uuid.NewV4().String(), + Policy: policy, + Entity: fluxHelmViolatingEntity, + Status: domain.PolicyValidationStatusViolating, + Message: "flux-helm-entity", + Type: "Admission", + Trigger: "Admission", + CreatedAt: time.Now(), + }, + { + ID: uuid.NewV4().String(), + Policy: policy, + Entity: fluxKustomizeViolatingEntity, + Status: domain.PolicyValidationStatusViolating, + Message: "flux-kustomize-entity", Type: "Admission", Trigger: "Admission", CreatedAt: time.Now(), @@ -93,35 +141,49 @@ func TestK8sEventSink(t *testing.T) { t.Error(err) } - time.Sleep(2 * time.Second) + time.Sleep(4 * time.Second) events, err := sink.kubeClient.CoreV1().Events("").List(ctx, metav1.ListOptions{}) if err != nil { t.Error(err) } - assert.Equal(t, len(events.Items), 2, "did not receive expected events") + assert.Equal(t, len(events.Items), 4, "did not receive expected events") for _, event := range events.Items { - if event.Type == v1.EventTypeWarning { + if event.Message == "violating-entity" { assert.Equal(t, event.Reason, domain.EventReasonPolicyViolation) assert.Equal(t, event.Action, domain.EventActionRejected) - // verify involved object holds entity info assert.Equal(t, event.InvolvedObject.APIVersion, violatingEntity.APIVersion) assert.Equal(t, event.InvolvedObject.Kind, violatingEntity.Kind) assert.Equal(t, event.InvolvedObject.Name, violatingEntity.Name) assert.Equal(t, event.InvolvedObject.Namespace, violatingEntity.Namespace) - } else if event.Type == v1.EventTypeNormal { + } else if event.Message == "compliant-entity" { assert.Equal(t, event.Reason, domain.EventReasonPolicyCompliance) assert.Equal(t, event.Action, domain.EventActionAllowed) - // verify involved object holds entity info assert.Equal(t, event.InvolvedObject.APIVersion, compliantEntity.APIVersion) assert.Equal(t, event.InvolvedObject.Kind, compliantEntity.Kind) assert.Equal(t, event.InvolvedObject.Name, compliantEntity.Name) assert.Equal(t, event.InvolvedObject.Namespace, compliantEntity.Namespace) + } else if event.Message == "flux-helm-entity" { + assert.Equal(t, event.Reason, domain.EventReasonPolicyViolation) + assert.Equal(t, event.Action, domain.EventActionRejected) + // verify involved object holds entity info + assert.Equal(t, event.InvolvedObject.APIVersion, "helm.toolkit.fluxcd.io") + assert.Equal(t, event.InvolvedObject.Kind, "HelmRelease") + assert.Equal(t, event.InvolvedObject.Name, "my-helm-app-name") + assert.Equal(t, event.InvolvedObject.Namespace, "my-helm-app-namespace") + } else if event.Message == "compliant-entity" { + assert.Equal(t, event.Reason, domain.EventReasonPolicyViolation) + assert.Equal(t, event.Action, domain.EventActionRejected) + // verify involved object holds entity info + assert.Equal(t, event.InvolvedObject.APIVersion, "kustomize.toolkit.fluxcd.io") + assert.Equal(t, event.InvolvedObject.Kind, "Kustomization") + assert.Equal(t, event.InvolvedObject.Name, "my-kustomize-app-name") + assert.Equal(t, event.InvolvedObject.Namespace, "my-kustomize-app-namespace") } // verify involved object holds entity info diff --git a/internal/sink/flux-notification/utils.go b/internal/utils/utils.go similarity index 83% rename from internal/sink/flux-notification/utils.go rename to internal/utils/utils.go index 43de6207..05e1ee72 100644 --- a/internal/sink/flux-notification/utils.go +++ b/internal/utils/utils.go @@ -1,10 +1,9 @@ -package flux_notification +package utils import ( "fmt" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" ) var fluxControllerKindMap = map[string]string{ @@ -12,7 +11,7 @@ var fluxControllerKindMap = map[string]string{ "kustomize.toolkit.fluxcd.io": "Kustomization", } -func getFluxObject(labels map[string]string) runtime.Object { +func GetFluxObject(labels map[string]string) *unstructured.Unstructured { for apiVersion, kind := range fluxControllerKindMap { name, ok := labels[fmt.Sprintf("%s/name", apiVersion)] if !ok { diff --git a/internal/utils/utils_test.go b/internal/utils/utils_test.go new file mode 100644 index 00000000..075285c5 --- /dev/null +++ b/internal/utils/utils_test.go @@ -0,0 +1,30 @@ +package utils + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetFluxObject(t *testing.T) { + fluxObj := GetFluxObject(map[string]string{}) + + if fluxObj != nil { + t.Error("unexpected flux object") + } + + for apiVersion, kind := range fluxControllerKindMap { + fluxObj := GetFluxObject(map[string]string{ + fmt.Sprintf("%s/name", apiVersion): "my-app", + fmt.Sprintf("%s/namespace", apiVersion): "default", + }) + + assert.NotEqual(t, fluxObj, nil) + + assert.Equal(t, fluxObj.GetAPIVersion(), apiVersion) + assert.Equal(t, fluxObj.GetKind(), kind) + assert.Equal(t, fluxObj.GetNamespace(), "default") + assert.Equal(t, fluxObj.GetName(), "my-app") + } +} From e4dc672e614731eb5f1dfa5884810fb15b271dc3 Mon Sep 17 00:00:00 2001 From: Ahmed El-Sayed Date: Mon, 10 Oct 2022 15:20:10 +0200 Subject: [PATCH 2/3] set event namespace as flux object namespace --- helm/Chart.yaml | 4 ++-- internal/sink/k8s-event/k8s_event.go | 1 + version.txt | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 5f4aaf33..f6a0a39f 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v2 -appVersion: "1.2.0" +appVersion: "1.2.1" description: A Helm chart for Kubernetes to configure the policy agent name: policy-agent -version: 1.2.0 +version: 1.2.1 maintainers: - name: Weaveworks email: support@weave.works diff --git a/internal/sink/k8s-event/k8s_event.go b/internal/sink/k8s-event/k8s_event.go index 93addd2e..ad1cc774 100644 --- a/internal/sink/k8s-event/k8s_event.go +++ b/internal/sink/k8s-event/k8s_event.go @@ -90,6 +90,7 @@ func (k *K8sEventSink) write(ctx context.Context, result domain.PolicyValidation Namespace: fluxObject.GetNamespace(), ResourceVersion: fluxObject.GetResourceVersion(), } + event.Namespace = fluxObject.GetNamespace() } if err != nil { diff --git a/version.txt b/version.txt index 867e5243..cb174d58 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.2.0 \ No newline at end of file +1.2.1 \ No newline at end of file From 603d62253e02e00951a6b70b4f1a95f2c3439e03 Mon Sep 17 00:00:00 2001 From: Ahmed El-Sayed Date: Mon, 10 Oct 2022 16:03:02 +0200 Subject: [PATCH 3/3] fix error handling in writing k8s events --- internal/sink/k8s-event/k8s_event.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/internal/sink/k8s-event/k8s_event.go b/internal/sink/k8s-event/k8s_event.go index ad1cc774..2a2873ac 100644 --- a/internal/sink/k8s-event/k8s_event.go +++ b/internal/sink/k8s-event/k8s_event.go @@ -79,6 +79,18 @@ func (f *K8sEventSink) writeWorker(ctx context.Context) error { func (k *K8sEventSink) write(ctx context.Context, result domain.PolicyValidation) { event, err := domain.NewK8sEventFromPolicyValidation(result) + if err != nil { + logger.Errorw( + "failed to create event from policy validation", + "error", + err, + "entity_kind", result.Entity.Kind, + "entity_name", result.Entity.Name, + "entity_namespace", result.Entity.Namespace, + "policy", result.Policy.ID, + ) + return + } fluxObject := utils.GetFluxObject(result.Entity.Labels) if fluxObject != nil { @@ -93,18 +105,6 @@ func (k *K8sEventSink) write(ctx context.Context, result domain.PolicyValidation event.Namespace = fluxObject.GetNamespace() } - if err != nil { - logger.Errorw( - "failed to create event from policy validation", - "error", - err, - "entity_kind", result.Entity.Kind, - "entity_name", result.Entity.Name, - "entity_namespace", result.Entity.Namespace, - "policy", result.Policy.ID, - ) - return - } event.ReportingController = k.reportingController event.ReportingInstance = k.reportingInstance event.Source = v1.EventSource{Component: k.reportingController}