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:
|
||||
yarn lint
|
||||
|
||||
test:
|
||||
test: download-bins
|
||||
yarn test
|
||||
|
||||
integration-linux:
|
||||
|
||||
@ -44,6 +44,10 @@ export class KubeAuthProxy {
|
||||
}
|
||||
logger.debug(`spawning kubectl proxy with args: ${args}`)
|
||||
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.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 { customRequest } from "../common/request";
|
||||
import { getBundledKubectlVersion } from "../common/utils/app-version"
|
||||
import { isDevelopment, isWindows } from "../common/vars";
|
||||
import { isDevelopment, isWindows, isTestEnv } from "../common/vars";
|
||||
|
||||
const bundledVersion = getBundledKubectlVersion()
|
||||
const kubectlMap: Map<string, string> = new Map([
|
||||
@ -35,8 +35,9 @@ const packageMirrors: Map<string, string> = new Map([
|
||||
let bundledPath: string
|
||||
const initScriptVersionString = "# lens-initscript v3\n"
|
||||
|
||||
if (isDevelopment) {
|
||||
bundledPath = path.join(process.cwd(), "binaries", "client", process.platform, process.arch, "kubectl")
|
||||
if (isDevelopment || isTestEnv) {
|
||||
const platformName = isWindows ? "windows" : process.platform
|
||||
bundledPath = path.join(process.cwd(), "binaries", "client", platformName, process.arch, "kubectl")
|
||||
} else {
|
||||
bundledPath = path.join(process.resourcesPath, process.arch, "kubectl")
|
||||
}
|
||||
@ -58,6 +59,7 @@ export class Kubectl {
|
||||
|
||||
public static readonly bundledKubectlPath = bundledPath
|
||||
public static readonly bundledKubectlVersion: string = bundledVersion
|
||||
public static invalidBundle = false
|
||||
private static bundledInstance: Kubectl;
|
||||
|
||||
// Returns the single bundled Kubectl instance
|
||||
@ -98,9 +100,22 @@ export class Kubectl {
|
||||
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 {
|
||||
await this.ensureKubectl()
|
||||
if (!await this.ensureKubectl()) {
|
||||
logger.error("Failed to ensure kubectl, fallback to the bundled version")
|
||||
return Kubectl.bundledKubectlPath
|
||||
}
|
||||
return this.path
|
||||
} catch (err) {
|
||||
logger.error("Failed to ensure kubectl, fallback to the bundled version")
|
||||
@ -119,16 +134,15 @@ export class Kubectl {
|
||||
}
|
||||
}
|
||||
|
||||
public async checkBinary(checkVersion = true) {
|
||||
const exists = await pathExists(this.path)
|
||||
public async checkBinary(path: string, checkVersion = true) {
|
||||
const exists = await pathExists(path)
|
||||
if (exists) {
|
||||
try {
|
||||
const { stdout } = await promiseExec(`"${path}" version --client=true -o json`)
|
||||
const output = JSON.parse(stdout)
|
||||
if (!checkVersion) {
|
||||
return true
|
||||
}
|
||||
|
||||
try {
|
||||
const { stdout } = await promiseExec(`"${this.path}" version --client=true -o json`)
|
||||
const output = JSON.parse(stdout)
|
||||
let version: string = output.clientVersion.gitVersion
|
||||
if (version[0] === 'v') {
|
||||
version = version.slice(1)
|
||||
@ -165,15 +179,28 @@ export class Kubectl {
|
||||
}
|
||||
|
||||
public async ensureKubectl(): Promise<boolean> {
|
||||
if (Kubectl.invalidBundle) {
|
||||
logger.error(`Detected invalid bundle binary, returning ...`)
|
||||
return false
|
||||
}
|
||||
await ensureDir(this.dirname, 0o755)
|
||||
return lockFile.lock(this.dirname).then(async (release) => {
|
||||
logger.debug(`Acquired a lock for ${this.kubectlVersion}`)
|
||||
const bundled = await this.checkBundled()
|
||||
const isValid = await this.checkBinary(!bundled)
|
||||
if (!isValid) {
|
||||
let isValid = await this.checkBinary(this.path, !bundled)
|
||||
if (!isValid && !bundled) {
|
||||
await this.downloadKubectl().catch((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) => {
|
||||
logger.error("Failed to write init scripts");
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import packageInfo from "../../package.json"
|
||||
import path from "path"
|
||||
import { bundledKubectl, Kubectl } from "../../src/main/kubectl";
|
||||
import { isWindows } from "../common/vars";
|
||||
|
||||
jest.mock("../common/user-store");
|
||||
|
||||
@ -15,3 +17,29 @@ describe("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