mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Detect invalid bundled kubectl (#778)
* Test bundled kubectl Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com>
This commit is contained in:
parent
f4d262b294
commit
533646cba5
2
Makefile
2
Makefile
@ -28,7 +28,7 @@ endif
|
|||||||
lint:
|
lint:
|
||||||
yarn lint
|
yarn lint
|
||||||
|
|
||||||
test:
|
test: download-bins
|
||||||
yarn test
|
yarn test
|
||||||
|
|
||||||
integration-linux:
|
integration-linux:
|
||||||
|
|||||||
@ -44,6 +44,10 @@ export class KubeAuthProxy {
|
|||||||
}
|
}
|
||||||
logger.debug(`spawning kubectl proxy with args: ${args}`)
|
logger.debug(`spawning kubectl proxy with args: ${args}`)
|
||||||
this.proxyProcess = spawn(proxyBin, args, { env: this.env, })
|
this.proxyProcess = spawn(proxyBin, args, { env: this.env, })
|
||||||
|
this.proxyProcess.on("error", (error) => {
|
||||||
|
this.sendIpcLogMessage({ data: error.message, error: true })
|
||||||
|
this.exit()
|
||||||
|
})
|
||||||
|
|
||||||
this.proxyProcess.on("exit", (code) => {
|
this.proxyProcess.on("exit", (code) => {
|
||||||
this.sendIpcLogMessage({ data: `proxy exited with code: ${code}`, error: code > 0 })
|
this.sendIpcLogMessage({ data: `proxy exited with code: ${code}`, error: code > 0 })
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { helmCli } from "./helm/helm-cli"
|
|||||||
import { userStore } from "../common/user-store"
|
import { userStore } from "../common/user-store"
|
||||||
import { customRequest } from "../common/request";
|
import { customRequest } from "../common/request";
|
||||||
import { getBundledKubectlVersion } from "../common/utils/app-version"
|
import { getBundledKubectlVersion } from "../common/utils/app-version"
|
||||||
import { isDevelopment, isWindows } from "../common/vars";
|
import { isDevelopment, isWindows, isTestEnv } from "../common/vars";
|
||||||
|
|
||||||
const bundledVersion = getBundledKubectlVersion()
|
const bundledVersion = getBundledKubectlVersion()
|
||||||
const kubectlMap: Map<string, string> = new Map([
|
const kubectlMap: Map<string, string> = new Map([
|
||||||
@ -35,8 +35,9 @@ const packageMirrors: Map<string, string> = new Map([
|
|||||||
let bundledPath: string
|
let bundledPath: string
|
||||||
const initScriptVersionString = "# lens-initscript v3\n"
|
const initScriptVersionString = "# lens-initscript v3\n"
|
||||||
|
|
||||||
if (isDevelopment) {
|
if (isDevelopment || isTestEnv) {
|
||||||
bundledPath = path.join(process.cwd(), "binaries", "client", process.platform, process.arch, "kubectl")
|
const platformName = isWindows ? "windows" : process.platform
|
||||||
|
bundledPath = path.join(process.cwd(), "binaries", "client", platformName, process.arch, "kubectl")
|
||||||
} else {
|
} else {
|
||||||
bundledPath = path.join(process.resourcesPath, process.arch, "kubectl")
|
bundledPath = path.join(process.resourcesPath, process.arch, "kubectl")
|
||||||
}
|
}
|
||||||
@ -58,6 +59,7 @@ export class Kubectl {
|
|||||||
|
|
||||||
public static readonly bundledKubectlPath = bundledPath
|
public static readonly bundledKubectlPath = bundledPath
|
||||||
public static readonly bundledKubectlVersion: string = bundledVersion
|
public static readonly bundledKubectlVersion: string = bundledVersion
|
||||||
|
public static invalidBundle = false
|
||||||
private static bundledInstance: Kubectl;
|
private static bundledInstance: Kubectl;
|
||||||
|
|
||||||
// Returns the single bundled Kubectl instance
|
// Returns the single bundled Kubectl instance
|
||||||
@ -98,9 +100,22 @@ export class Kubectl {
|
|||||||
this.path = path.join(this.dirname, binaryName)
|
this.path = path.join(this.dirname, binaryName)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getPath(): Promise<string> {
|
public getBundledPath() {
|
||||||
|
return Kubectl.bundledKubectlPath
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getPath(bundled = false): Promise<string> {
|
||||||
|
// return binary name if bundled path is not functional
|
||||||
|
if (!await this.checkBinary(this.getBundledPath(), false)) {
|
||||||
|
Kubectl.invalidBundle = true
|
||||||
|
return path.basename(bundledPath)
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.ensureKubectl()
|
if (!await this.ensureKubectl()) {
|
||||||
|
logger.error("Failed to ensure kubectl, fallback to the bundled version")
|
||||||
|
return Kubectl.bundledKubectlPath
|
||||||
|
}
|
||||||
return this.path
|
return this.path
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error("Failed to ensure kubectl, fallback to the bundled version")
|
logger.error("Failed to ensure kubectl, fallback to the bundled version")
|
||||||
@ -119,16 +134,15 @@ export class Kubectl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async checkBinary(checkVersion = true) {
|
public async checkBinary(path: string, checkVersion = true) {
|
||||||
const exists = await pathExists(this.path)
|
const exists = await pathExists(path)
|
||||||
if (exists) {
|
if (exists) {
|
||||||
if (!checkVersion) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { stdout } = await promiseExec(`"${this.path}" version --client=true -o json`)
|
const { stdout } = await promiseExec(`"${path}" version --client=true -o json`)
|
||||||
const output = JSON.parse(stdout)
|
const output = JSON.parse(stdout)
|
||||||
|
if (!checkVersion) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
let version: string = output.clientVersion.gitVersion
|
let version: string = output.clientVersion.gitVersion
|
||||||
if (version[0] === 'v') {
|
if (version[0] === 'v') {
|
||||||
version = version.slice(1)
|
version = version.slice(1)
|
||||||
@ -165,15 +179,28 @@ export class Kubectl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async ensureKubectl(): Promise<boolean> {
|
public async ensureKubectl(): Promise<boolean> {
|
||||||
|
if (Kubectl.invalidBundle) {
|
||||||
|
logger.error(`Detected invalid bundle binary, returning ...`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
await ensureDir(this.dirname, 0o755)
|
await ensureDir(this.dirname, 0o755)
|
||||||
return lockFile.lock(this.dirname).then(async (release) => {
|
return lockFile.lock(this.dirname).then(async (release) => {
|
||||||
logger.debug(`Acquired a lock for ${this.kubectlVersion}`)
|
logger.debug(`Acquired a lock for ${this.kubectlVersion}`)
|
||||||
const bundled = await this.checkBundled()
|
const bundled = await this.checkBundled()
|
||||||
const isValid = await this.checkBinary(!bundled)
|
let isValid = await this.checkBinary(this.path, !bundled)
|
||||||
if (!isValid) {
|
if (!isValid && !bundled) {
|
||||||
await this.downloadKubectl().catch((error) => {
|
await this.downloadKubectl().catch((error) => {
|
||||||
logger.error(error)
|
logger.error(error)
|
||||||
|
logger.debug(`Releasing lock for ${this.kubectlVersion}`)
|
||||||
|
release()
|
||||||
|
return false
|
||||||
});
|
});
|
||||||
|
isValid = !await this.checkBinary(this.path, false)
|
||||||
|
}
|
||||||
|
if(!isValid) {
|
||||||
|
logger.debug(`Releasing lock for ${this.kubectlVersion}`)
|
||||||
|
release()
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
await this.writeInitScripts().catch((error) => {
|
await this.writeInitScripts().catch((error) => {
|
||||||
logger.error("Failed to write init scripts");
|
logger.error("Failed to write init scripts");
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import packageInfo from "../../package.json"
|
import packageInfo from "../../package.json"
|
||||||
|
import path from "path"
|
||||||
import { bundledKubectl, Kubectl } from "../../src/main/kubectl";
|
import { bundledKubectl, Kubectl } from "../../src/main/kubectl";
|
||||||
|
import { isWindows } from "../common/vars";
|
||||||
|
|
||||||
jest.mock("../common/user-store");
|
jest.mock("../common/user-store");
|
||||||
|
|
||||||
@ -15,3 +17,29 @@ describe("kubectlVersion", () => {
|
|||||||
expect(kubectl.kubectlVersion).toBe(bundledKubectl.kubectlVersion)
|
expect(kubectl.kubectlVersion).toBe(bundledKubectl.kubectlVersion)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("getPath()", () => {
|
||||||
|
it("returns path to downloaded kubectl binary", async () => {
|
||||||
|
const { bundledKubectlVersion } = packageInfo.config;
|
||||||
|
const kubectl = new Kubectl(bundledKubectlVersion);
|
||||||
|
const kubectlPath = await kubectl.getPath()
|
||||||
|
let binaryName = "kubectl"
|
||||||
|
if (isWindows) {
|
||||||
|
binaryName += ".exe"
|
||||||
|
}
|
||||||
|
const expectedPath = path.join(Kubectl.kubectlDir, Kubectl.bundledKubectlVersion, binaryName)
|
||||||
|
expect(kubectlPath).toBe(expectedPath)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns plain binary name if bundled kubectl is non-functional", async () => {
|
||||||
|
const { bundledKubectlVersion } = packageInfo.config;
|
||||||
|
const kubectl = new Kubectl(bundledKubectlVersion);
|
||||||
|
jest.spyOn(kubectl, "getBundledPath").mockReturnValue("/invalid/path/kubectl")
|
||||||
|
const kubectlPath = await kubectl.getPath()
|
||||||
|
let binaryName = "kubectl"
|
||||||
|
if (isWindows) {
|
||||||
|
binaryName += ".exe"
|
||||||
|
}
|
||||||
|
expect(kubectlPath).toBe(binaryName)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user