Skip to content

Commit

Permalink
feat: handle params
Browse files Browse the repository at this point in the history
  • Loading branch information
topliceanurazvan committed Sep 28, 2023
1 parent 06ae34f commit 719293a
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 9 deletions.
32 changes: 31 additions & 1 deletion packages/validation/src/__tests__/MonokleValidator.vap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ import {
VALIDATING_ADMISSION_POLICY,
VALIDATING_ADMISSION_POLICY_BINDING,
DEPLOYMENT,
} from './admissionPolicyValidatorResources.js';
} from './resources/admissionPolicy/BasicValidatorResources.js';
import {
PARAMS_CONFIG_MAP,
PARAMS_DEPLOYMENT,
PARAMS_NAMESPACE,
PARAMS_VALIDATING_ADMISSION_POLICY,
PARAMS_VALIDATING_ADMISSION_POLICY_BINDING,
} from './resources/admissionPolicy/ParamsValidatorResources.js';

it('test basic admission policy', async () => {
const parser = new ResourceParser();
Expand All @@ -29,6 +36,29 @@ it('test basic admission policy', async () => {
expect(hasErrors).toBe(1);
});

it('test params admission policy', async () => {
const parser = new ResourceParser();

const validator = createTestValidator(parser, {
plugins: {
'admission-policy': true,
},
});

const response = await validator.validate({
resources: [
PARAMS_NAMESPACE,
PARAMS_VALIDATING_ADMISSION_POLICY,
PARAMS_VALIDATING_ADMISSION_POLICY_BINDING,
PARAMS_DEPLOYMENT,
PARAMS_CONFIG_MAP,
],
});

const hasErrors = response.runs.reduce((sum, r) => sum + r.results.length, 0);
expect(hasErrors).toBe(1);
});

function createTestValidator(parser: ResourceParser, config?: ValidationConfig) {
return new MonokleValidator(
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Resource} from '../index.js';
import {Resource} from '../../../common/types.js';

export const VALIDATING_ADMISSION_POLICY: Resource = {
fileId: '18acb3eb78d60',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import {Resource} from '../../../common/types.js';

export const PARAMS_CONFIG_MAP: Resource = {
fileId: '174d42e877a174',
filePath: 'config-map.yaml',
fileOffset: 0,
text: 'apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: rule-config\n namespace: demo\nmaxReplicas: 5\n',
apiVersion: 'v1',
kind: 'ConfigMap',
content: {
apiVersion: 'v1',
kind: 'ConfigMap',
metadata: {
name: 'rule-config',
namespace: 'demo',
},
maxReplicas: 5,
},
id: '174d42e877a174-0',
name: 'rule-config',
namespace: 'demo',
};

export const PARAMS_DEPLOYMENT: Resource = {
fileId: '2f92c46b9eb02',
filePath: 'deployment.yaml',
fileOffset: 0,
text: 'apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: nginx-deployment\n namespace: demo\n labels:\n app: nginx\nspec:\n replicas: 5\n selector:\n matchLabels:\n app: nginx\n template:\n metadata:\n labels:\n app: nginx\n spec:\n containers:\n - name: nginx\n image: nginx:1.14.2\n ports:\n - containerPort: 80\n',
apiVersion: 'apps/v1',
kind: 'Deployment',
content: {
apiVersion: 'apps/v1',
kind: 'Deployment',
metadata: {
name: 'nginx-deployment',
namespace: 'demo',
labels: {
app: 'nginx',
},
},
spec: {
replicas: 4,
selector: {
matchLabels: {
app: 'nginx',
},
},
template: {
metadata: {
labels: {
app: 'nginx',
},
},
spec: {
containers: [
{
name: 'nginx',
image: 'nginx:1.14.2',
ports: [
{
containerPort: 80,
},
],
},
],
},
},
},
},
id: '2f92c46b9eb02-0',
name: 'nginx-deployment',
namespace: 'demo',
};

export const PARAMS_NAMESPACE: Resource = {
fileId: '1926d9cf253e4c',
filePath: 'namespace.yaml',
fileOffset: 0,
text: 'apiVersion: v1\nkind: Namespace\nmetadata:\n name: demo\n labels:\n environment: test\n',
apiVersion: 'v1',
kind: 'Namespace',
content: {
apiVersion: 'v1',
kind: 'Namespace',
metadata: {
name: 'demo',
labels: {
environment: 'test',
},
},
},
id: '1926d9cf253e4c-0',
name: 'demo',
};

export const PARAMS_VALIDATING_ADMISSION_POLICY: Resource = {
fileId: '18acb3eb78d60',
filePath: 'policy.yaml',
fileOffset: 0,
text: 'apiVersion: admissionregistration.k8s.io/v1beta1\nkind: ValidatingAdmissionPolicy\nmetadata:\n name: "demo-policy.example.com"\nspec:\n paramKind:\n apiVersion: v1\n kind: ConfigMap\n matchConstraints:\n resourceRules:\n - apiGroups: [ "apps" ]\n apiVersions: [ "v1" ]\n operations: [ "CREATE", "UPDATE" ]\n resources: [ "deployments" ]\n validations:\n - expression: "object.spec.replicas > 6"\n',
apiVersion: 'admissionregistration.k8s.io/v1beta1',
kind: 'ValidatingAdmissionPolicy',
content: {
apiVersion: 'admissionregistration.k8s.io/v1beta1',
kind: 'ValidatingAdmissionPolicy',
metadata: {
name: 'demo-policy.example.com',
},
spec: {
paramKind: {
apiVersion: 'v1',
kind: 'ConfigMap',
},
matchConstraints: {
resourceRules: [
{
apiGroups: ['apps'],
apiVersions: ['v1'],
operations: ['CREATE', 'UPDATE'],
resources: ['deployments'],
},
],
},
validations: [
{
expression: 'object.spec.replicas > params.maxReplicas',
},
],
},
},
id: '18acb3eb78d60-0',
name: 'demo-policy.example.com',
};

export const PARAMS_VALIDATING_ADMISSION_POLICY_BINDING: Resource = {
fileId: '19b972898cc6be',
filePath: 'policy-binding.yaml',
fileOffset: 0,
text: 'apiVersion: admissionregistration.k8s.io/v1beta1\nkind: ValidatingAdmissionPolicyBinding\nmetadata:\n name: "demo-binding-test.example.com"\nspec:\n policyName: "demo-policy.example.com"\n paramRef:\n name: rule-config\n namespace: demo\n validationActions: [ Deny ]\n matchResources:\n namespaceSelector:\n matchLabels:\n environment: test\n',
apiVersion: 'admissionregistration.k8s.io/v1beta1',
kind: 'ValidatingAdmissionPolicyBinding',
content: {
apiVersion: 'admissionregistration.k8s.io/v1beta1',
kind: 'ValidatingAdmissionPolicyBinding',
metadata: {
name: 'demo-binding-test.example.com',
},
spec: {
policyName: 'demo-policy.example.com',
paramRef: {
name: 'rule-config',
namespace: 'demo',
},
validationActions: ['Deny'],
matchResources: {
namespaceSelector: {
matchLabels: {
environment: 'test',
},
},
},
},
},
id: '19b972898cc6be-0',
name: 'demo-binding-test.example.com',
};
12 changes: 10 additions & 2 deletions packages/validation/src/validators/admission-policy/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ export type Expression = {

export type PolicyExpressionsAndFilteredResources = Record<
string,
{expressions: Expression[]; resources: Resource[]; level: RuleLevel}
{expressions: Expression[]; resources: Resource[]; level: RuleLevel; params?: any}
>;

export type PolicyBindingFilterResponse = Record<string, {resources: Resource[]; level: RuleLevel}>;
export type ParamRef = {
name: string;
namespace: string;
};

export type PolicyBindingFilterResponse = Record<
string,
{resources: Resource[]; level: RuleLevel; paramRef?: ParamRef}
>;
31 changes: 26 additions & 5 deletions packages/validation/src/validators/admission-policy/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ export class AdmissionPolicyValidator extends AbstractPlugin {

const resourcesToBeValidated = this.getResourcesToBeValidated(resources);

for (const [policyName, {resources: filteredResources, expressions, level}] of Object.entries(
for (const [policyName, {resources: filteredResources, expressions, level, params}] of Object.entries(
resourcesToBeValidated
)) {
for (const resource of filteredResources) {
for (const expression of expressions) {
const errors = await this.validateResource(resource, expression, level);
const errors = await this.validateResource(resource, expression, level, params);

results.push(...errors);
}
Expand All @@ -60,9 +60,10 @@ export class AdmissionPolicyValidator extends AbstractPlugin {
private async validateResource(
resource: Resource,
{message, expression}: Expression,
level: RuleLevel
level: RuleLevel,
params?: any
): Promise<ValidationResult[]> {
const output = (globalThis as any).eval(expression, YAML.stringify({object: resource.content})).output;
const output = (globalThis as any).eval(expression, YAML.stringify({object: resource.content, params})).output;

if (output === 'true' || output.includes('ERROR:')) {
return [];
Expand Down Expand Up @@ -133,6 +134,7 @@ export class AdmissionPolicyValidator extends AbstractPlugin {
policyFilteredResources[policyName] = {
resources: filteredResources,
level: policyBinding.content?.spec?.validationActions?.includes('Deny') ? 'error' : 'warning',
paramRef: policyBinding.content?.spec?.paramRef,
};
}

Expand All @@ -145,7 +147,7 @@ export class AdmissionPolicyValidator extends AbstractPlugin {
): PolicyExpressionsAndFilteredResources {
const filteredResourcesWithExpressions: PolicyExpressionsAndFilteredResources = {};

for (const [policyName, {resources, level}] of Object.entries(policyFilteredResources)) {
for (const [policyName, {resources, level, paramRef}] of Object.entries(policyFilteredResources)) {
const policy = policies.find(p => p.name === policyName);

if (!policy) continue;
Expand Down Expand Up @@ -188,13 +190,32 @@ export class AdmissionPolicyValidator extends AbstractPlugin {

if (!filteredResourcesByPolicy.length) continue;

let params: any;

const paramKind = policy.content?.spec?.paramKind;

if (paramKind && paramRef) {
const paramResource = resources.find(
r =>
r.kind === paramKind?.kind &&
r.apiVersion === paramKind?.apiVersion &&
r.content?.metadata?.name === paramRef.name &&
r.content?.metadata?.namespace === paramRef.namespace
);

if (paramResource) {
params = paramResource.content;
}
}

filteredResourcesWithExpressions[policy.name] = {
resources: filteredResourcesByPolicy,
expressions: validations.map((v: any) => ({
expression: v.expression,
message: v.message,
})),
level,
params,
};
}

Expand Down

0 comments on commit 719293a

Please sign in to comment.