Skip to content

Commit

Permalink
refactor: resolve eslint warnings (max-depth, complexity) - src/lib/v…
Browse files Browse the repository at this point in the history
…alidate-processor.ts (#1529)

### Describe what should be investigated or refactored

Refactor of `src/lib/validate-processor.ts` to reduce complexity
warnings:
```
/pepr/src/lib/validate-processor.ts
  15:8  warning  Async function 'validateProcessor' has a complexity of 13. Maximum allowed is 10  complexity
  62:9  warning  Blocks are nested too deeply (4). Maximum allowed is 3                            max-depth

```

### Additional context
Fixes #1381 
In support of #1248
  • Loading branch information
btlghrants authored Dec 9, 2024
1 parent 9d5beb1 commit 35024c4
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 39 deletions.
103 changes: 103 additions & 0 deletions src/lib/validate-processor.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors

import { beforeEach, describe, expect, it, jest } from "@jest/globals";
import { GroupVersionKind, kind, KubernetesObject } from "kubernetes-fluent-client";
import { AdmissionRequest, Binding, Filters } from "./types";
import { Event, Operation } from "./enums";
import { PeprValidateRequest } from "./validate-request";
import { clone } from "ramda";
import * as sut from "./validate-processor";

const testFilters: Filters = {
annotations: {},
deletionTimestamp: false,
labels: {},
name: "",
namespaces: [],
regexName: "^default$",
regexNamespaces: [] as string[],
};

const testGroupVersionKind: GroupVersionKind = {
kind: "some-kind",
group: "some-group",
};

const testBinding: Binding = {
event: Event.ANY,
filters: testFilters,
kind: testGroupVersionKind,
model: kind.Pod,
isFinalize: false,
isMutate: false,
isQueue: false,
isValidate: false,
isWatch: false,
};

export const testAdmissionRequest: AdmissionRequest = {
uid: "some-uid",
kind: { kind: "a-kind", group: "a-group" },
resource: { group: "some-group", version: "some-version", resource: "some-resource" },
operation: Operation.CONNECT,
name: "some-name",
userInfo: {},
object: {},
};

export const testActionMetadata: Record<string, string> = {};

export const testPeprValidateRequest = (admissionRequest: AdmissionRequest) =>
new PeprValidateRequest<KubernetesObject>(admissionRequest);

describe("processRequest", () => {
let binding: Binding;
let actionMetadata: Record<string, string>;
let peprValidateRequest: PeprValidateRequest<KubernetesObject>;

beforeEach(() => {
binding = clone(testBinding);
actionMetadata = clone(testActionMetadata);
peprValidateRequest = testPeprValidateRequest(testAdmissionRequest);
});

it("responds on successful validation action", async () => {
const cbResult = {
allowed: true,
statusCode: 200,
statusMessage: "yay",
};
const callback = jest.fn().mockImplementation(() => cbResult) as Binding["validateCallback"];
binding = { ...clone(testBinding), validateCallback: callback };

const result = await sut.processRequest(binding, actionMetadata, peprValidateRequest);

expect(result).toEqual({
uid: peprValidateRequest.Request.uid,
allowed: cbResult.allowed,
status: {
code: cbResult.statusCode,
message: cbResult.statusMessage,
},
});
});

it("responds on unsuccessful validation action", async () => {
const callback = jest.fn().mockImplementation(() => {
throw "oof";
}) as Binding["validateCallback"];
binding = { ...clone(testBinding), validateCallback: callback };

const result = await sut.processRequest(binding, actionMetadata, peprValidateRequest);

expect(result).toEqual({
uid: peprValidateRequest.Request.uid,
allowed: false,
status: {
code: 500,
message: `Action failed with error: "oof"`,
},
});
});
});
86 changes: 47 additions & 39 deletions src/lib/validate-processor.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,56 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors

import { kind } from "kubernetes-fluent-client";

import { kind, KubernetesObject } from "kubernetes-fluent-client";
import { Capability } from "./capability";
import { shouldSkipRequest } from "./filter/filter";
import { ValidateResponse } from "./k8s";
import { AdmissionRequest } from "./types";
import { AdmissionRequest, Binding } from "./types";
import Log from "./telemetry/logger";
import { convertFromBase64Map } from "./utils";
import { PeprValidateRequest } from "./validate-request";
import { ModuleConfig } from "./module";

export async function processRequest(
binding: Binding,
actionMetadata: Record<string, string>,
peprValidateRequest: PeprValidateRequest<KubernetesObject>,
): Promise<ValidateResponse> {
const label = binding.validateCallback!.name;
Log.info(actionMetadata, `Processing validation action (${label})`);

const valResp: ValidateResponse = {
uid: peprValidateRequest.Request.uid,
allowed: true, // Assume it's allowed until a validation check fails
};

try {
// Run the validation callback, if it fails set allowed to false
const callbackResp = await binding.validateCallback!(peprValidateRequest);
valResp.allowed = callbackResp.allowed;

// If the validation callback returned a status code or message, set it in the Response
if (callbackResp.statusCode || callbackResp.statusMessage) {
valResp.status = {
code: callbackResp.statusCode || 400,
message: callbackResp.statusMessage || `Validation failed for ${name}`,
};
}

Log.info(actionMetadata, `Validation action complete (${label}): ${callbackResp.allowed ? "allowed" : "denied"}`);
return valResp;
} catch (e) {
// If any validation throws an error, note the failure in the Response
Log.error(actionMetadata, `Action failed: ${JSON.stringify(e)}`);
valResp.allowed = false;
valResp.status = {
code: 500,
message: `Action failed with error: ${JSON.stringify(e)}`,
};
return valResp;
}
}

export async function validateProcessor(
config: ModuleConfig,
capabilities: Capability[],
Expand All @@ -32,52 +71,21 @@ export async function validateProcessor(
for (const { name, bindings, namespaces } of capabilities) {
const actionMetadata = { ...reqMetadata, name };

for (const action of bindings) {
for (const binding of bindings) {
// Skip this action if it's not a validation action
if (!action.validateCallback) {
if (!binding.validateCallback) {
continue;
}

const localResponse: ValidateResponse = {
uid: req.uid,
allowed: true, // Assume it's allowed until a validation check fails
};

// Continue to the next action without doing anything if this one should be skipped
const shouldSkip = shouldSkipRequest(action, req, namespaces, config?.alwaysIgnore?.namespaces);
const shouldSkip = shouldSkipRequest(binding, req, namespaces, config?.alwaysIgnore?.namespaces);
if (shouldSkip !== "") {
Log.debug(shouldSkip);
continue;
}

const label = action.validateCallback.name;
Log.info(actionMetadata, `Processing validation action (${label})`);

try {
// Run the validation callback, if it fails set allowed to false
const resp = await action.validateCallback(wrapped);
localResponse.allowed = resp.allowed;

// If the validation callback returned a status code or message, set it in the Response
if (resp.statusCode || resp.statusMessage) {
localResponse.status = {
code: resp.statusCode || 400,
message: resp.statusMessage || `Validation failed for ${name}`,
};
}

Log.info(actionMetadata, `Validation action complete (${label}): ${resp.allowed ? "allowed" : "denied"}`);
} catch (e) {
// If any validation throws an error, note the failure in the Response
Log.error(actionMetadata, `Action failed: ${JSON.stringify(e)}`);
localResponse.allowed = false;
localResponse.status = {
code: 500,
message: `Action failed with error: ${JSON.stringify(e)}`,
};
return [localResponse];
}
response.push(localResponse);
const resp = await processRequest(binding, actionMetadata, wrapped);
response.push(resp);
}
}

Expand Down

0 comments on commit 35024c4

Please sign in to comment.