From 7cd8d5e20ec2029da7d81e1d3ee2853c492a9191 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Fri, 24 Sep 2021 13:23:55 +0300 Subject: [PATCH] detect log follow requests Signed-off-by: Jari Kolehmainen --- src/main/__test__/lens-proxy.test.ts | 56 ++++++++++++++++++++++++++++ src/main/context-handler.ts | 6 +-- src/main/lens-proxy.ts | 21 ++++++++++- 3 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 src/main/__test__/lens-proxy.test.ts diff --git a/src/main/__test__/lens-proxy.test.ts b/src/main/__test__/lens-proxy.test.ts new file mode 100644 index 0000000000..3f0b97d860 --- /dev/null +++ b/src/main/__test__/lens-proxy.test.ts @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { isLongRunningRequest } from "../lens-proxy"; + +describe("isLongRunningRequest", () => { + it("returns true on watches", () => { + ["watch=true", "watch=1"].forEach((param) => { + expect( + isLongRunningRequest(`/api/v1/namespaces/default/pods?${param}`) + ).toBeTruthy(); + }); + }); + + it("returns false on disabled watches", () => { + ["watch=false", "watch=0", "watch", ""].forEach((param) => { + expect( + isLongRunningRequest(`/api/v1/namespaces/default/pods?${param}`) + ).toBeFalsy(); + }); + }); + + it("returns true on follows", () => { + ["follow=true", "follow=1"].forEach((param) => { + expect( + isLongRunningRequest(`/api/v1/namespaces/default/pods/foo/log?${param}`) + ).toBeTruthy(); + }); + }); + + it("returns false on disabled follows", () => { + ["follow=false", "follow=0", "follow", ""].forEach((param) => { + expect( + isLongRunningRequest(`/api/v1/namespaces/default/pods/foo/log?${param}`) + ).toBeFalsy(); + }); + }); +}); diff --git a/src/main/context-handler.ts b/src/main/context-handler.ts index 82c365e821..b6e14df4e5 100644 --- a/src/main/context-handler.ts +++ b/src/main/context-handler.ts @@ -117,10 +117,10 @@ export class ContextHandler { return `http://127.0.0.1:${this.kubeAuthProxy.port}${path}`; } - async getApiTarget(isWatchRequest = false): Promise { - const timeout = isWatchRequest ? 4 * 60 * 60 * 1000 : 30000; // 4 hours for watch request, 30 seconds for the rest + async getApiTarget(isLongRunningRequest = false): Promise { + const timeout = isLongRunningRequest ? 4 * 60 * 60_000 : 30_000; // 4 hours for long running request, 30 seconds for the rest - if (isWatchRequest) { + if (isLongRunningRequest) { return this.newApiTarget(timeout); } diff --git a/src/main/lens-proxy.ts b/src/main/lens-proxy.ts index 3cd5182677..7d6a703270 100644 --- a/src/main/lens-proxy.ts +++ b/src/main/lens-proxy.ts @@ -40,6 +40,24 @@ export interface LensProxyFunctions { kubeApiRequest: (args: ProxyApiRequestArgs) => void | Promise; } +const truthyParams = ["1", "true"]; +const watchParam = "watch"; +const followParam = "follow"; + +export function isLongRunningRequest(reqUrl: string) { + const url = new URL(reqUrl, "http://localhost"); + + if (url.searchParams.has(watchParam) && truthyParams.includes(url.searchParams.get(watchParam)) ) { + return true; + } + + if (url.searchParams.has(followParam) && truthyParams.includes(url.searchParams.get(followParam)) ) { + return true; + } + + return false; +} + export class LensProxy extends Singleton { protected origin: string; protected proxyServer: http.Server; @@ -174,9 +192,8 @@ export class LensProxy extends Singleton { if (req.url.startsWith(apiKubePrefix)) { delete req.headers.authorization; req.url = req.url.replace(apiKubePrefix, ""); - const isWatchRequest = req.url.includes("watch="); - return contextHandler.getApiTarget(isWatchRequest); + return contextHandler.getApiTarget(isLongRunningRequest(req.url)); } }