import packageInfo from "../package.json" import fs from "fs" import request from "request" import md5File from "md5-file" import requestPromise from "request-promise-native" import { ensureDir, pathExists } from "fs-extra" import path from "path" class KubectlDownloader { public kubectlVersion: string protected url: string protected path: string; protected dirname: string constructor(clusterVersion: string, platform: string, arch: string, target: string) { this.kubectlVersion = clusterVersion; const binaryName = platform === "windows" ? "kubectl.exe" : "kubectl" this.url = `https://storage.googleapis.com/kubernetes-release/release/v${this.kubectlVersion}/bin/${platform}/${arch}/${binaryName}`; this.dirname = path.dirname(target); this.path = target; } protected async urlEtag() { const response = await requestPromise({ method: "HEAD", uri: this.url, resolveWithFullResponse: true }).catch((error) => { console.log(error) }) if (response.headers["etag"]) { return response.headers["etag"].replace(/"/g, "") } return "" } public async checkBinary() { const exists = await pathExists(this.path) if (exists) { const hash = md5File.sync(this.path) const etag = await this.urlEtag() if(hash == etag) { console.log("Kubectl md5sum matches the remote etag") return true } console.log("Kubectl md5sum " + hash + " does not match the remote etag " + etag + ", unlinking and downloading again") await fs.promises.unlink(this.path) } return false } public async downloadKubectl() { const exists = await this.checkBinary(); if(exists) { console.log("Already exists and is valid") return } await ensureDir(path.dirname(this.path), 0o755) const file = fs.createWriteStream(this.path) console.log(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`) const requestOpts: request.UriOptions & request.CoreOptions = { uri: this.url, gzip: true } const stream = request(requestOpts) stream.on("complete", () => { console.log("kubectl binary download finished") file.end(() => {}) }) stream.on("error", (error) => { console.log(error) fs.unlink(this.path, () => {}) throw(error) }) return new Promise((resolve, reject) => { file.on("close", () => { console.log("kubectl binary download closed") fs.chmod(this.path, 0o755, (err) => { if (err) reject(err); }) resolve() }) stream.pipe(file) }) } } const downloadVersion = packageInfo.config.bundledKubectlVersion; const baseDir = path.join(process.env.INIT_CWD, 'binaries', 'client') const downloads = [ { platform: 'linux', arch: 'amd64', target: path.join(baseDir, 'linux', 'x64', 'kubectl') }, { platform: 'darwin', arch: 'amd64', target: path.join(baseDir, 'darwin', 'x64', 'kubectl') }, { platform: 'windows', arch: 'amd64', target: path.join(baseDir, 'windows', 'x64', 'kubectl.exe') }, { platform: 'windows', arch: '386', target: path.join(baseDir, 'windows', 'ia32', 'kubectl.exe') } ] downloads.forEach((dlOpts) => { console.log(dlOpts) const downloader = new KubectlDownloader(downloadVersion, dlOpts.platform, dlOpts.arch, dlOpts.target); console.log("Downloading: " + JSON.stringify(dlOpts)); downloader.downloadKubectl().then(() => downloader.checkBinary().then(() => console.log("Download complete"))) })