diff --git a/admission-controller/synchronizer/src/utils/policy-updater.ts b/admission-controller/synchronizer/src/utils/policy-updater.ts index e99c610..f18936f 100644 --- a/admission-controller/synchronizer/src/utils/policy-updater.ts +++ b/admission-controller/synchronizer/src/utils/policy-updater.ts @@ -241,7 +241,7 @@ export class PolicyUpdater { } protected mapValidationAction(action: string) { - const actionNormalized = action.toLowerCase().trim(); + const actionNormalized = (action || '').toLowerCase().trim(); switch (actionNormalized) { case 'warn': @@ -249,8 +249,8 @@ export class PolicyUpdater { case 'deny': return 'Deny'; default: - this._logger.error({ msg: 'Unknown validation action', action }); - return 'Warn'; + this._logger.error({ msg: 'Unknown validation action.', action }); + return action; } } } diff --git a/tests/src/cloud.e2e.spec.ts b/tests/src/cloud.e2e.spec.ts index 52d56e6..99d8789 100644 --- a/tests/src/cloud.e2e.spec.ts +++ b/tests/src/cloud.e2e.spec.ts @@ -81,6 +81,21 @@ describe(`Cloud (dir: ${mainDir})`, () => { assertResource(policy2, 'cluster-1-binding-2-policy'); }, 45 * 1000); + it('correctly maps deny action', async () => { + mockServer = await startMockServer('actionDeny'); + + // Wait for getCluster query to run. + await waitForRequests(mockServer, 2); + // Wait for CRDs propagation. + await sleep(500); + + const policy1 = await run('kubectl get monoklepolicy.monokle.io/cluster-1-binding-1-policy -o yaml'); + const binding1 = await run('kubectl get monoklepolicybinding.monokle.io/cluster-1-binding-1-deny -o yaml'); + + assertResource(binding1, 'cluster-1-binding-1-deny'); + assertResource(policy1, 'cluster-1-binding-1-policy'); + }, 45 * 1000); + // @TODO updates policy CRDs with new data // @TODO deletes policy CRDs // @TODO updates binding CRDs with new data diff --git a/tests/src/utils/expected-crds.ts b/tests/src/utils/expected-crds.ts index 79d73fd..45730e2 100644 --- a/tests/src/utils/expected-crds.ts +++ b/tests/src/utils/expected-crds.ts @@ -19,6 +19,26 @@ export const EXPECTED_CRDS: Record = { } } }, + 'cluster-1-binding-1-deny': { + apiVersion: 'monokle.io/v1alpha1', + kind: 'MonoklePolicyBinding', + metadata: { + name: 'cluster-1-binding-1' + }, + spec: { + policyName: 'cluster-1-binding-1-policy', + validationActions: ['Deny'], + matchResources: { + namespaceSelector: { + matchExpressions: [{ + key: 'name', + operator: 'In', + values: ['my-namespace-0'], + }] + } + } + } + }, 'cluster-1-binding-2': { apiVersion: 'monokle.io/v1alpha1', kind: 'MonoklePolicyBinding', diff --git a/tests/src/utils/response-mocks.ts b/tests/src/utils/response-mocks.ts index e3bde15..ec575f3 100644 --- a/tests/src/utils/response-mocks.ts +++ b/tests/src/utils/response-mocks.ts @@ -45,6 +45,7 @@ export const RESPONSE_MOCK: Record = { { id: "cluster-1-binding-1", mode: "ALLOW_LIST", + action: "warn", namespaces: ["ns-0","ns-1"], policy: { id: "cluster-1-binding-1-policy", @@ -58,6 +59,7 @@ export const RESPONSE_MOCK: Record = { { id: "cluster-1-binding-2", mode: "ALLOW_LIST", + action: "warn", namespaces: ["ns-2","ns-1"], policy: { id: "cluster-1-binding-2-policy", @@ -71,5 +73,36 @@ export const RESPONSE_MOCK: Record = { ] } } + }, + actionDeny: { + data: { + getCluster: { + id: "cluster-1", + name: "Cluster 1", + namespaceSync: true, + namespaces: [ + { + id: "ns-0", + name: "my-namespace-0" + } + ], + bindings: [ + { + id: "cluster-1-binding-1-deny", + mode: "ALLOW_LIST", + action: "deny", + namespaces: ["ns-0"], + policy: { + id: "cluster-1-binding-1-policy", + content: "plugins:\n open-policy-agent: true\n pod-security-standards: true\n", + project: { + id: "cluster-1-binding-1-policy-project", + name: "cluster-1-binding-1-policy-project" + } + } + } + ] + } + } } } diff --git a/tests/src/utils/server.ts b/tests/src/utils/server.ts index 5e3eef6..cedf3c9 100644 --- a/tests/src/utils/server.ts +++ b/tests/src/utils/server.ts @@ -4,7 +4,7 @@ import cors from 'cors'; import _ from 'lodash'; import {RESPONSE_MOCK} from './response-mocks.js'; -type ResponseMockName = 'empty' | 'emptySync' | 'dataAllow' | 'dataBlock'; +type ResponseMockName = 'empty' | 'emptySync' | 'dataAllow' | 'actionDeny'; type MockServer = { server: Server;