Skip to content

Commit

Permalink
Apply formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
gergas3 committed Oct 25, 2023
1 parent 994af98 commit 25afe6d
Show file tree
Hide file tree
Showing 15 changed files with 122 additions and 135 deletions.
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

yarn run lint-staged
5 changes: 2 additions & 3 deletions cli/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import yargs from "yargs";
import { hideBin } from "yargs/helpers";

import { JsonRpcClient } from "../client";
import { Backoff } from "../client/backoff";
import { createLogger } from "../log";
import { runApp } from "../server";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";

const logger = createLogger({ name: "cli" });

Expand Down
56 changes: 46 additions & 10 deletions client/__tests__/__snapshots__/filter.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,32 +1,68 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`json path filter returns only selected fields 1`] = `
exports[`json path filter returns complete data on error 1`] = `
{
"data": {
"apiVersion": "rbac.authorization.k8s.io/v1",
"items": [
{
"metadata": {
"managedFields": [
{
"manager": "clusterrole-aggregation-controller",
"operation": "Apply",
},
{
"manager": "kube-apiserver",
"operation": "Update",
},
],
"name": "admin",
"resourceVersion": "345",
"uid": "4251609f-b3ca-4875-8ec6-76b40ab62748",
},
},
{
"metadata": {
"managedFields": [
{
"manager": "kubectl-client-side-apply",
"operation": "Update",
},
],
"name": "aws-node",
"resourceVersion": "273",
"uid": "63a3bed8-a750-440d-bca9-b5f41c8786c2",
},
},
],
"kind": "ClusterRoleList",
},
"headers": {
"audit-id": "6a816d03-d29f-4657-b624-35766c8a5572",
"cache-control": "no-cache, private",
"content-type": "application/json",
"date": "Wed, 25 Oct 2023 06:08:30 GMT",
"transfer-encoding": "chunked",
"x-kubernetes-pf-flowschema-uid": "ef7c3d21-4649-422d-a4fa-e5b85d52f141",
"x-kubernetes-pf-prioritylevel-uid": "a6704cd4-c4fb-48bb-982e-a1ed79bfb12d",
"metadata": {
"resourceVersion": "4423772",
},
},
"status": 200,
"statusText": "OK",
}
`;

exports[`json path filter returns only selected fields 1`] = `
{
"data": {
"items": [
{
"metadata": {
"name": "admin",
},
},
{
"metadata": {
"name": "aws-node",
},
},
],
"kind": "ClusterRoleList",
},
"status": 200,
}
`;
87 changes: 15 additions & 72 deletions client/__tests__/filter.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
import { jpFilter as jqFilter } from "client/filter";

const exampleResponse = {
headers: {
"audit-id": "6a816d03-d29f-4657-b624-35766c8a5572",
"cache-control": "no-cache, private",
"content-type": "application/json",
"x-kubernetes-pf-flowschema-uid": "ef7c3d21-4649-422d-a4fa-e5b85d52f141",
"x-kubernetes-pf-prioritylevel-uid": "a6704cd4-c4fb-48bb-982e-a1ed79bfb12d",
date: "Wed, 25 Oct 2023 06:08:30 GMT",
"transfer-encoding": "chunked",
},
status: 200,
statusText: "OK",
data: {
Expand All @@ -24,46 +15,14 @@ const exampleResponse = {
name: "admin",
uid: "4251609f-b3ca-4875-8ec6-76b40ab62748",
resourceVersion: "345",
creationTimestamp: "2023-10-02T22:51:23Z",
labels: {
"kubernetes.io/bootstrapping": "rbac-defaults",
},
annotations: {
"rbac.authorization.kubernetes.io/autoupdate": "true",
},
managedFields: [
{
manager: "clusterrole-aggregation-controller",
operation: "Apply",
apiVersion: "rbac.authorization.k8s.io/v1",
time: "2023-10-02T22:51:38Z",
fieldsType: "FieldsV1",
fieldsV1: {
"f:rules": {},
},
},
{
manager: "kube-apiserver",
operation: "Update",
apiVersion: "rbac.authorization.k8s.io/v1",
time: "2023-10-02T22:51:23Z",
fieldsType: "FieldsV1",
fieldsV1: {
"f:aggregationRule": {
".": {},
"f:clusterRoleSelectors": {},
},
"f:metadata": {
"f:annotations": {
".": {},
"f:rbac.authorization.kubernetes.io/autoupdate": {},
},
"f:labels": {
".": {},
"f:kubernetes.io/bootstrapping": {},
},
},
},
},
],
},
Expand All @@ -73,40 +32,10 @@ const exampleResponse = {
name: "aws-node",
uid: "63a3bed8-a750-440d-bca9-b5f41c8786c2",
resourceVersion: "273",
creationTimestamp: "2023-10-02T22:51:35Z",
labels: {
"app.kubernetes.io/instance": "aws-vpc-cni",
"app.kubernetes.io/name": "aws-node",
"app.kubernetes.io/version": "v1.12.6",
"k8s-app": "aws-node",
},
annotations: {
"kubectl.kubernetes.io/last-applied-configuration":
'{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"aws-vpc-cni","app.kubernetes.io/name":"aws-node","app.kubernetes.io/version":"v1.12.6","k8s-app":"aws-node"},"name":"aws-node"},"rules":[{"apiGroups":["crd.k8s.amazonaws.com"],"resources":["eniconfigs"],"verbs":["list","watch","get"]},{"apiGroups":[""],"resources":["namespaces"],"verbs":["list","watch","get"]},{"apiGroups":[""],"resources":["pods"],"verbs":["list","watch","get"]},{"apiGroups":[""],"resources":["nodes"],"verbs":["list","watch","get","update"]},{"apiGroups":["extensions"],"resources":["*"],"verbs":["list","watch"]},{"apiGroups":["","events.k8s.io"],"resources":["events"],"verbs":["create","patch","list"]}]}\n',
},
managedFields: [
{
manager: "kubectl-client-side-apply",
operation: "Update",
apiVersion: "rbac.authorization.k8s.io/v1",
time: "2023-10-02T22:51:35Z",
fieldsType: "FieldsV1",
fieldsV1: {
"f:metadata": {
"f:annotations": {
".": {},
"f:kubectl.kubernetes.io/last-applied-configuration": {},
},
"f:labels": {
".": {},
"f:app.kubernetes.io/instance": {},
"f:app.kubernetes.io/name": {},
"f:app.kubernetes.io/version": {},
"f:k8s-app": {},
},
},
"f:rules": {},
},
},
],
},
Expand All @@ -119,9 +48,23 @@ describe("json path filter", () => {
it("returns only selected fields", async () => {
const result = await jqFilter(
exampleResponse,
"{ headers: .headers, status: .status, statusText: .statusText, data: { kind: .data.kind, items: [.data.items[] | { metadata: { name: .metadata.name } }] } }"
"{ status: .status, data: { kind: .data.kind, items: [.data.items[] | { metadata: { name: .metadata.name } }] } }"
);

expect(result).toMatchSnapshot();
});

describe("returns original input data", () => {
it("on error", async () => {
const result = await jqFilter(exampleResponse, "{ { }");

expect(result).toMatchSnapshot();
});

it("when no filter supplied", async () => {
const result = await jqFilter(exampleResponse, undefined);

expect(result).toMatchSnapshot();
});
});
});
17 changes: 12 additions & 5 deletions client/filter.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import { createLogger } from "../log";
import { isArray } from "lodash";

const logger = createLogger({ name: "filter" });

// jq-node doesn't support `import` syntax
const jq = require("node-jq");

export const jpFilter = async (
data: any,
jqHeader: string | string[] | undefined
): Promise<any> => {
const jpSelectQuery = isArray(jqHeader) ? jqHeader[0] : jqHeader;
if (!jpSelectQuery) {
const query = isArray(jqHeader) ? jqHeader[0] : jqHeader;
if (!query) {
return data;
}
try {
return await jq.run(jpSelectQuery, data, { input: "json", output: "json" });
} catch (e: any) {
console.log(e);
// jq.run() returns a Promise
return await jq.run(query, data, { input: "json", output: "json" });
} catch (error: any) {
logger.error(
{ error, jpSelectQuery: query },
"Error running jq query, ignoring filters"
);
return data;
}
};
31 changes: 17 additions & 14 deletions client/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import { DEFAULT_FORWARDED_REQUEST_TIMEOUT_MILLIS } from "../common/constants";
import { createLogger } from "../log";
import { ForwardedRequest, ForwardedResponse, JQ_HEADER } from "../types";
import { deferral } from "../util/deferral";
import { Backoff } from "./backoff";
import { jpFilter as jqFilter } from "./filter";
import { jwt } from "./jwks";
import axios from "axios";
import {
JSONRPCClient,
Expand All @@ -8,14 +15,6 @@ import { isArray, omit } from "lodash";
import { Logger } from "pino";
import WebSocket from "ws";

import { DEFAULT_FORWARDED_REQUEST_TIMEOUT_MILLIS } from "../common/constants";
import { createLogger } from "../log";
import { ForwardedRequest, ForwardedResponse, JQ_HEADER } from "../types";
import { deferral } from "../util/deferral";
import { Backoff } from "./backoff";
import { jpFilter as jqFilter } from "./filter";
import { jwt } from "./jwks";

/**
* Bi-directional JSON RPC client
*/
Expand Down Expand Up @@ -63,11 +62,7 @@ export class JsonRpcClient {
});

axios.interceptors.response.use((response) => {
// Do not log request object, it's lengthy and difficult to filter out the authorization header
this.#logger.debug(
{ response: omit(response, "request") },
"Axios response"
);
this.#logger.debug({ response }, "Axios response");
return response;
});
}
Expand Down Expand Up @@ -135,7 +130,15 @@ export class JsonRpcClient {
this.#webSocket = clientSocket;

client.addMethod("call", async (request: ForwardedRequest) => {
this.#logger.debug({ request }, "forwarded request");
this.#logger.debug(
{
request: {
...request,
headers: omit(request.headers, "authorization"),
},
},
"forwarded request"
);
// The headers are modified:
// 1. The Content-Length header may not be accurate for the forwarded request. By removing it, axios can recalculate the correct length.
// 2. The Host header should be switched out to the host this client is targeting.
Expand Down
3 changes: 1 addition & 2 deletions client/jwks.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { privateKeyFile, publicKeyFile } from "../util/jwk-file";
import * as jose from "jose";
import * as fs from "node:fs/promises";
import pinoLogger from "pino";

import { privateKeyFile, publicKeyFile } from "../util/jwk-file";

const ALG = "ES384"; // Elliptic curve with 384-bit SHA

const logger = pinoLogger({ name: "jwks" });
Expand Down
1 change: 1 addition & 0 deletions log/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const createLogger = <T extends LoggerOptions>(
// Redact the authorization header that may contain a secret token
redact: [
"response.config.headers.authorization", // Axios intercepted response object
"response.request.headers.authorization", // Axios intercepted response object contains the request as well
"request.headers.authorization", // Axios intercepted request object + forwarded request object (JSON RPC request)
],
formatters: {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-n": "^15.0.0",
"eslint-plugin-promise": "^6.0.0",
"husky": "^8.0.2",
"husky": "^8.0.3",
"jest": "29.2.1",
"jest-extended": "3.2.4",
"jest-mock": "29.2.1",
Expand Down Expand Up @@ -63,7 +63,7 @@
"start:prod:server": "NODE_PATH=dist node dist/cli server",
"start:dev:client": "NODE_ENV=development nodemon cli client",
"start:prod:client": "NODE_PATH=dist node dist/cli client",
"prepare": "cd .. && husky install ts/.husky",
"prepare": "husky install",
"format": "prettier --write '**/*.{html,js,ts,json,less,md}'",
"format:quick": "pretty-quick --branch origin/main"
},
Expand Down
5 changes: 2 additions & 3 deletions server/__tests__/e2e.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { Server } from "http";
import request from "supertest";

import { App, InitContext, runApp } from "../";
// TODO replace supertest with axios requests
import { JsonRpcClient } from "../../client";
import { Backoff } from "../../client/backoff";
import { testHttpServer } from "../testing/testExpressApp";
import { Server } from "http";
import request from "supertest";

const SERVER_RPC_PORT = 8080;
const SERVER_PROXY_PORT = 8081;
Expand Down
3 changes: 1 addition & 2 deletions server/auth.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { PublicKeyGetter } from "../types";
import * as jose from "jose";
import pinoLogger from "pino";

import { PublicKeyGetter } from "../types";

const AUTH_PATTERN = /Bearer (.*)/;
const ALG = "ES384";

Expand Down
Loading

0 comments on commit 25afe6d

Please sign in to comment.