mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Allow user to configure kubectl binary preferences (#800)
Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> Co-authored-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
parent
8f94f4a3da
commit
4250523fe4
@ -1,4 +1,5 @@
|
|||||||
import type { ThemeId } from "../renderer/theme.store";
|
import type { ThemeId } from "../renderer/theme.store";
|
||||||
|
import { app, remote } from 'electron';
|
||||||
import semver from "semver"
|
import semver from "semver"
|
||||||
import { readFile } from "fs-extra"
|
import { readFile } from "fs-extra"
|
||||||
import { action, observable, reaction, toJS } from "mobx";
|
import { action, observable, reaction, toJS } from "mobx";
|
||||||
@ -8,6 +9,7 @@ import { getAppVersion } from "./utils/app-version";
|
|||||||
import { kubeConfigDefaultPath, loadConfig } from "./kube-helpers";
|
import { kubeConfigDefaultPath, loadConfig } from "./kube-helpers";
|
||||||
import { tracker } from "./tracker";
|
import { tracker } from "./tracker";
|
||||||
import logger from "../main/logger";
|
import logger from "../main/logger";
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
export interface UserStoreModel {
|
export interface UserStoreModel {
|
||||||
kubeConfigPath: string;
|
kubeConfigPath: string;
|
||||||
@ -22,6 +24,9 @@ export interface UserPreferences {
|
|||||||
allowUntrustedCAs?: boolean;
|
allowUntrustedCAs?: boolean;
|
||||||
allowTelemetry?: boolean;
|
allowTelemetry?: boolean;
|
||||||
downloadMirror?: string | "default";
|
downloadMirror?: string | "default";
|
||||||
|
downloadKubectlBinaries?: boolean;
|
||||||
|
downloadBinariesPath?: string;
|
||||||
|
kubectlBinariesPath?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserStore extends BaseStore<UserStoreModel> {
|
export class UserStore extends BaseStore<UserStoreModel> {
|
||||||
@ -53,6 +58,9 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
allowUntrustedCAs: false,
|
allowUntrustedCAs: false,
|
||||||
colorTheme: UserStore.defaultTheme,
|
colorTheme: UserStore.defaultTheme,
|
||||||
downloadMirror: "default",
|
downloadMirror: "default",
|
||||||
|
downloadKubectlBinaries: true, // Download kubectl binaries matching cluster version
|
||||||
|
downloadBinariesPath: this.getDefaultKubectlPath(),
|
||||||
|
kubectlBinariesPath: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
get isNewVersion() {
|
get isNewVersion() {
|
||||||
@ -98,6 +106,14 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
this.newContexts.clear();
|
this.newContexts.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting default directory to download kubectl binaries
|
||||||
|
* @returns string
|
||||||
|
*/
|
||||||
|
getDefaultKubectlPath(): string {
|
||||||
|
return path.join((app || remote.app).getPath("userData"), "binaries")
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
protected async fromStore(data: Partial<UserStoreModel> = {}) {
|
protected async fromStore(data: Partial<UserStoreModel> = {}) {
|
||||||
const { lastSeenAppVersion, seenContexts = [], preferences, kubeConfigPath } = data
|
const { lastSeenAppVersion, seenContexts = [], preferences, kubeConfigPath } = data
|
||||||
|
|||||||
@ -97,7 +97,7 @@ export class Kubectl {
|
|||||||
|
|
||||||
this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${platformName}/${arch}/${binaryName}`
|
this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${platformName}/${arch}/${binaryName}`
|
||||||
|
|
||||||
this.dirname = path.normalize(path.join(Kubectl.kubectlDir, this.kubectlVersion))
|
this.dirname = path.normalize(path.join(this.getDownloadDir(), this.kubectlVersion))
|
||||||
this.path = path.join(this.dirname, binaryName)
|
this.path = path.join(this.dirname, binaryName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +105,19 @@ export class Kubectl {
|
|||||||
return Kubectl.bundledKubectlPath
|
return Kubectl.bundledKubectlPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getPathFromPreferences() {
|
||||||
|
return userStore.preferences?.kubectlBinariesPath || this.getBundledPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getDownloadDir() {
|
||||||
|
return userStore.preferences?.downloadBinariesPath || Kubectl.kubectlDir
|
||||||
|
}
|
||||||
|
|
||||||
public async getPath(bundled = false): Promise<string> {
|
public async getPath(bundled = false): Promise<string> {
|
||||||
|
if (userStore.preferences?.downloadKubectlBinaries === false) {
|
||||||
|
return this.getPathFromPreferences()
|
||||||
|
}
|
||||||
|
|
||||||
// return binary name if bundled path is not functional
|
// return binary name if bundled path is not functional
|
||||||
if (!await this.checkBinary(this.getBundledPath(), false)) {
|
if (!await this.checkBinary(this.getBundledPath(), false)) {
|
||||||
Kubectl.invalidBundle = true
|
Kubectl.invalidBundle = true
|
||||||
@ -128,6 +140,7 @@ export class Kubectl {
|
|||||||
public async binDir() {
|
public async binDir() {
|
||||||
try {
|
try {
|
||||||
await this.ensureKubectl()
|
await this.ensureKubectl()
|
||||||
|
await this.writeInitScripts()
|
||||||
return this.dirname
|
return this.dirname
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
@ -180,6 +193,9 @@ export class Kubectl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async ensureKubectl(): Promise<boolean> {
|
public async ensureKubectl(): Promise<boolean> {
|
||||||
|
if (userStore.preferences?.downloadKubectlBinaries === false) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
if (Kubectl.invalidBundle) {
|
if (Kubectl.invalidBundle) {
|
||||||
logger.error(`Detected invalid bundle binary, returning ...`)
|
logger.error(`Detected invalid bundle binary, returning ...`)
|
||||||
return false
|
return false
|
||||||
@ -203,10 +219,6 @@ export class Kubectl {
|
|||||||
release()
|
release()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
await this.writeInitScripts().catch((error) => {
|
|
||||||
logger.error("Failed to write init scripts");
|
|
||||||
logger.error(error)
|
|
||||||
})
|
|
||||||
logger.debug(`Releasing lock for ${this.kubectlVersion}`)
|
logger.debug(`Releasing lock for ${this.kubectlVersion}`)
|
||||||
release()
|
release()
|
||||||
return true
|
return true
|
||||||
@ -249,28 +261,12 @@ export class Kubectl {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async scriptIsLatest(scriptPath: string) {
|
|
||||||
const scriptExists = await pathExists(scriptPath)
|
|
||||||
if (!scriptExists) return false
|
|
||||||
|
|
||||||
try {
|
|
||||||
const filehandle = await fs.promises.open(scriptPath, 'r')
|
|
||||||
const buffer = Buffer.alloc(40)
|
|
||||||
await filehandle.read(buffer, 0, 40, 0)
|
|
||||||
await filehandle.close()
|
|
||||||
return buffer.toString().startsWith(initScriptVersionString)
|
|
||||||
} catch (err) {
|
|
||||||
logger.error(err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async writeInitScripts() {
|
protected async writeInitScripts() {
|
||||||
|
const kubectlPath = userStore.preferences?.downloadKubectlBinaries ? this.dirname : path.dirname(this.getPathFromPreferences())
|
||||||
const helmPath = helmCli.getBinaryDir()
|
const helmPath = helmCli.getBinaryDir()
|
||||||
const fsPromises = fs.promises;
|
const fsPromises = fs.promises;
|
||||||
const bashScriptPath = path.join(this.dirname, '.bash_set_path')
|
const bashScriptPath = path.join(this.dirname, '.bash_set_path')
|
||||||
const bashScriptIsLatest = await this.scriptIsLatest(bashScriptPath)
|
|
||||||
if (!bashScriptIsLatest) {
|
|
||||||
let bashScript = "" + initScriptVersionString
|
let bashScript = "" + initScriptVersionString
|
||||||
bashScript += "tempkubeconfig=\"$KUBECONFIG\"\n"
|
bashScript += "tempkubeconfig=\"$KUBECONFIG\"\n"
|
||||||
bashScript += "test -f \"/etc/profile\" && . \"/etc/profile\"\n"
|
bashScript += "test -f \"/etc/profile\" && . \"/etc/profile\"\n"
|
||||||
@ -281,15 +277,13 @@ export class Kubectl {
|
|||||||
bashScript += "elif test -f \"$HOME/.profile\"; then\n"
|
bashScript += "elif test -f \"$HOME/.profile\"; then\n"
|
||||||
bashScript += " . \"$HOME/.profile\"\n"
|
bashScript += " . \"$HOME/.profile\"\n"
|
||||||
bashScript += "fi\n"
|
bashScript += "fi\n"
|
||||||
bashScript += `export PATH="${this.dirname}:${helmPath}:$PATH"\n`
|
bashScript += `export PATH="${helmPath}:${kubectlPath}:$PATH"\n`
|
||||||
bashScript += "export KUBECONFIG=\"$tempkubeconfig\"\n"
|
bashScript += "export KUBECONFIG=\"$tempkubeconfig\"\n"
|
||||||
bashScript += "unset tempkubeconfig\n"
|
bashScript += "unset tempkubeconfig\n"
|
||||||
await fsPromises.writeFile(bashScriptPath, bashScript.toString(), { mode: 0o644 })
|
await fsPromises.writeFile(bashScriptPath, bashScript.toString(), { mode: 0o644 })
|
||||||
}
|
|
||||||
|
|
||||||
const zshScriptPath = path.join(this.dirname, '.zlogin')
|
const zshScriptPath = path.join(this.dirname, '.zlogin')
|
||||||
const zshScriptIsLatest = await this.scriptIsLatest(zshScriptPath)
|
|
||||||
if (!zshScriptIsLatest) {
|
|
||||||
let zshScript = "" + initScriptVersionString
|
let zshScript = "" + initScriptVersionString
|
||||||
|
|
||||||
zshScript += "tempkubeconfig=\"$KUBECONFIG\"\n"
|
zshScript += "tempkubeconfig=\"$KUBECONFIG\"\n"
|
||||||
@ -302,19 +296,18 @@ export class Kubectl {
|
|||||||
zshScript += "test -f \"$OLD_ZDOTDIR/.zshrc\" && . \"$OLD_ZDOTDIR/.zshrc\"\n"
|
zshScript += "test -f \"$OLD_ZDOTDIR/.zshrc\" && . \"$OLD_ZDOTDIR/.zshrc\"\n"
|
||||||
|
|
||||||
// voodoo to replace any previous occurrences of kubectl path in the PATH
|
// voodoo to replace any previous occurrences of kubectl path in the PATH
|
||||||
zshScript += `kubectlpath=\"${this.dirname}"\n`
|
zshScript += `kubectlpath=\"${kubectlPath}"\n`
|
||||||
zshScript += `helmpath=\"${helmPath}"\n`
|
zshScript += `helmpath=\"${helmPath}"\n`
|
||||||
zshScript += "p=\":$kubectlpath:\"\n"
|
zshScript += "p=\":$kubectlpath:\"\n"
|
||||||
zshScript += "d=\":$PATH:\"\n"
|
zshScript += "d=\":$PATH:\"\n"
|
||||||
zshScript += "d=${d//$p/:}\n"
|
zshScript += "d=${d//$p/:}\n"
|
||||||
zshScript += "d=${d/#:/}\n"
|
zshScript += "d=${d/#:/}\n"
|
||||||
zshScript += "export PATH=\"$kubectlpath:$helmpath:${d/%:/}\"\n"
|
zshScript += "export PATH=\"$helmpath:$kubectlpath:${d/%:/}\"\n"
|
||||||
zshScript += "export KUBECONFIG=\"$tempkubeconfig\"\n"
|
zshScript += "export KUBECONFIG=\"$tempkubeconfig\"\n"
|
||||||
zshScript += "unset tempkubeconfig\n"
|
zshScript += "unset tempkubeconfig\n"
|
||||||
zshScript += "unset OLD_ZDOTDIR\n"
|
zshScript += "unset OLD_ZDOTDIR\n"
|
||||||
await fsPromises.writeFile(zshScriptPath, zshScript.toString(), { mode: 0o644 })
|
await fsPromises.writeFile(zshScriptPath, zshScript.toString(), { mode: 0o644 })
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected getDownloadMirror() {
|
protected getDownloadMirror() {
|
||||||
const mirror = packageMirrors.get(userStore.preferences?.downloadMirror)
|
const mirror = packageMirrors.get(userStore.preferences?.downloadMirror)
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { ClusterPreferences } from "../common/cluster-store";
|
|||||||
import { helmCli } from "./helm/helm-cli"
|
import { helmCli } from "./helm/helm-cli"
|
||||||
import { isWindows } from "../common/vars";
|
import { isWindows } from "../common/vars";
|
||||||
import { tracker } from "../common/tracker";
|
import { tracker } from "../common/tracker";
|
||||||
|
import { userStore } from "../common/user-store";
|
||||||
|
|
||||||
export class ShellSession extends EventEmitter {
|
export class ShellSession extends EventEmitter {
|
||||||
static shellEnvs: Map<string, any> = new Map()
|
static shellEnvs: Map<string, any> = new Map()
|
||||||
@ -20,6 +21,7 @@ export class ShellSession extends EventEmitter {
|
|||||||
protected nodeShellPod: string;
|
protected nodeShellPod: string;
|
||||||
protected kubectl: Kubectl;
|
protected kubectl: Kubectl;
|
||||||
protected kubectlBinDir: string;
|
protected kubectlBinDir: string;
|
||||||
|
protected kubectlPathDir: string;
|
||||||
protected helmBinDir: string;
|
protected helmBinDir: string;
|
||||||
protected preferences: ClusterPreferences;
|
protected preferences: ClusterPreferences;
|
||||||
protected running = false;
|
protected running = false;
|
||||||
@ -36,6 +38,8 @@ export class ShellSession extends EventEmitter {
|
|||||||
|
|
||||||
public async open() {
|
public async open() {
|
||||||
this.kubectlBinDir = await this.kubectl.binDir()
|
this.kubectlBinDir = await this.kubectl.binDir()
|
||||||
|
const pathFromPreferences = userStore.preferences.kubectlBinariesPath || Kubectl.bundledKubectlPath
|
||||||
|
this.kubectlPathDir = userStore.preferences.downloadKubectlBinaries ? await this.kubectl.binDir() : path.dirname(pathFromPreferences)
|
||||||
this.helmBinDir = helmCli.getBinaryDir()
|
this.helmBinDir = helmCli.getBinaryDir()
|
||||||
const env = await this.getCachedShellEnv()
|
const env = await this.getCachedShellEnv()
|
||||||
const shell = env.PTYSHELL
|
const shell = env.PTYSHELL
|
||||||
@ -67,11 +71,11 @@ export class ShellSession extends EventEmitter {
|
|||||||
protected async getShellArgs(shell: string): Promise<Array<string>> {
|
protected async getShellArgs(shell: string): Promise<Array<string>> {
|
||||||
switch(path.basename(shell)) {
|
switch(path.basename(shell)) {
|
||||||
case "powershell.exe":
|
case "powershell.exe":
|
||||||
return ["-NoExit", "-command", `& {Set-Location $Env:USERPROFILE; $Env:PATH="${this.kubectlBinDir};${this.helmBinDir};$Env:PATH"}`]
|
return ["-NoExit", "-command", `& {Set-Location $Env:USERPROFILE; $Env:PATH="${this.helmBinDir};${this.kubectlPathDir};$Env:PATH"}`]
|
||||||
case "bash":
|
case "bash":
|
||||||
return ["--init-file", path.join(this.kubectlBinDir, '.bash_set_path')]
|
return ["--init-file", path.join(this.kubectlBinDir, '.bash_set_path')]
|
||||||
case "fish":
|
case "fish":
|
||||||
return ["--login", "--init-command", `export PATH="${this.kubectlBinDir}:${this.helmBinDir}:$PATH"; export KUBECONFIG="${this.kubeconfigPath}"`]
|
return ["--login", "--init-command", `export PATH="${this.helmBinDir}:${this.kubectlPathDir}:$PATH"; export KUBECONFIG="${this.kubeconfigPath}"`]
|
||||||
case "zsh":
|
case "zsh":
|
||||||
return ["--login"]
|
return ["--login"]
|
||||||
default:
|
default:
|
||||||
|
|||||||
83
src/renderer/components/+preferences/kubectl-binaries.tsx
Normal file
83
src/renderer/components/+preferences/kubectl-binaries.tsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Trans } from '@lingui/macro';
|
||||||
|
import { isPath } from '../input/input.validators';
|
||||||
|
import { Checkbox } from '../checkbox';
|
||||||
|
import { Input } from '../input';
|
||||||
|
import { SubTitle } from '../layout/sub-title';
|
||||||
|
import { UserPreferences, userStore } from '../../../common/user-store';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { Kubectl } from '../../../main/kubectl';
|
||||||
|
import { SelectOption, Select } from '../select';
|
||||||
|
|
||||||
|
export const KubectlBinaries = observer(({ preferences }: { preferences: UserPreferences }) => {
|
||||||
|
const [downloadPath, setDownloadPath] = useState(preferences.downloadBinariesPath || "");
|
||||||
|
const [binariesPath, setBinariesPath] = useState(preferences.kubectlBinariesPath || "");
|
||||||
|
|
||||||
|
const downloadMirrorOptions: SelectOption<string>[] = [
|
||||||
|
{ value: "default", label: "Default (Google)" },
|
||||||
|
{ value: "china", label: "China (Azure)" },
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
const save = () => {
|
||||||
|
preferences.downloadBinariesPath = downloadPath;
|
||||||
|
preferences.kubectlBinariesPath = binariesPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderPath = () => {
|
||||||
|
if (preferences.downloadKubectlBinaries) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SubTitle title="Path to Kubectl binary"/>
|
||||||
|
<Input
|
||||||
|
theme="round-black"
|
||||||
|
value={binariesPath}
|
||||||
|
validators={isPath}
|
||||||
|
onChange={setBinariesPath}
|
||||||
|
onBlur={save}
|
||||||
|
/>
|
||||||
|
<small className="hint">
|
||||||
|
<Trans>Default:</Trans>{" "}{Kubectl.bundledKubectlPath}
|
||||||
|
</small>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h2><Trans>Kubectl Binary</Trans></h2>
|
||||||
|
<small className="hint">
|
||||||
|
<Trans>Download kubectl binaries matching to Kubernetes cluster verison.</Trans>
|
||||||
|
</small>
|
||||||
|
<Checkbox
|
||||||
|
label={<Trans>Download kubectl binaries</Trans>}
|
||||||
|
value={preferences.downloadKubectlBinaries}
|
||||||
|
onChange={downloadKubectlBinaries => preferences.downloadKubectlBinaries = downloadKubectlBinaries}
|
||||||
|
/>
|
||||||
|
<SubTitle title="Download mirror" />
|
||||||
|
<Select
|
||||||
|
placeholder={<Trans>Download mirror for kubectl</Trans>}
|
||||||
|
options={downloadMirrorOptions}
|
||||||
|
value={preferences.downloadMirror}
|
||||||
|
onChange={({ value }: SelectOption) => preferences.downloadMirror = value}
|
||||||
|
disabled={!preferences.downloadKubectlBinaries}
|
||||||
|
/>
|
||||||
|
<SubTitle title="Directory for binaries"/>
|
||||||
|
<Input
|
||||||
|
theme="round-black"
|
||||||
|
value={downloadPath}
|
||||||
|
placeholder={`Directory to download binaries into`}
|
||||||
|
validators={isPath}
|
||||||
|
onChange={setDownloadPath}
|
||||||
|
onBlur={save}
|
||||||
|
disabled={!preferences.downloadKubectlBinaries}
|
||||||
|
/>
|
||||||
|
<small>
|
||||||
|
Default: {userStore.getDefaultKubectlPath()}
|
||||||
|
</small>
|
||||||
|
{renderPath()}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
@ -19,6 +19,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.SubTitle {
|
||||||
|
text-transform: none;
|
||||||
|
margin: 0!important;
|
||||||
|
}
|
||||||
|
|
||||||
.repos {
|
.repos {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
|||||||
@ -16,18 +16,13 @@ import { Badge } from "../badge";
|
|||||||
import { themeStore } from "../../theme.store";
|
import { themeStore } from "../../theme.store";
|
||||||
import { history } from "../../navigation";
|
import { history } from "../../navigation";
|
||||||
import { Tooltip } from "../tooltip";
|
import { Tooltip } from "../tooltip";
|
||||||
|
import { KubectlBinaries } from "./kubectl-binaries";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class Preferences extends React.Component {
|
export class Preferences extends React.Component {
|
||||||
@observable helmLoading = false;
|
@observable helmLoading = false;
|
||||||
@observable helmRepos: HelmRepo[] = [];
|
@observable helmRepos: HelmRepo[] = [];
|
||||||
@observable helmAddedRepos = observable.map<string, HelmRepo>();
|
@observable helmAddedRepos = observable.map<string, HelmRepo>();
|
||||||
|
|
||||||
@observable downloadMirrorOptions: SelectOption<string>[] = [
|
|
||||||
{ value: "default", label: "Default (Google)" },
|
|
||||||
{ value: "china", label: "China (Azure)" },
|
|
||||||
]
|
|
||||||
|
|
||||||
@observable httpProxy = userStore.preferences.httpsProxy || "";
|
@observable httpProxy = userStore.preferences.httpsProxy || "";
|
||||||
|
|
||||||
@computed get themeOptions(): SelectOption<string>[] {
|
@computed get themeOptions(): SelectOption<string>[] {
|
||||||
@ -135,13 +130,19 @@ export class Preferences extends React.Component {
|
|||||||
onChange={({ value }: SelectOption) => preferences.colorTheme = value}
|
onChange={({ value }: SelectOption) => preferences.colorTheme = value}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<h2><Trans>Download Mirror</Trans></h2>
|
<h2><Trans>HTTP Proxy</Trans></h2>
|
||||||
<Select
|
<Input
|
||||||
placeholder={<Trans>Download mirror for kubectl</Trans>}
|
theme="round-black"
|
||||||
options={this.downloadMirrorOptions}
|
placeholder={_i18n._(t`Type HTTP proxy url (example: http://proxy.acme.org:8080)`)}
|
||||||
value={preferences.downloadMirror}
|
value={this.httpProxy}
|
||||||
onChange={({ value }: SelectOption) => preferences.downloadMirror = value}
|
onChange={v => this.httpProxy = v}
|
||||||
|
onBlur={() => preferences.httpsProxy = this.httpProxy}
|
||||||
/>
|
/>
|
||||||
|
<small className="hint">
|
||||||
|
<Trans>Proxy is used only for non-cluster communication.</Trans>
|
||||||
|
</small>
|
||||||
|
|
||||||
|
<KubectlBinaries preferences={preferences} />
|
||||||
|
|
||||||
<h2><Trans>Helm</Trans></h2>
|
<h2><Trans>Helm</Trans></h2>
|
||||||
<Select
|
<Select
|
||||||
@ -172,18 +173,6 @@ export class Preferences extends React.Component {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2><Trans>HTTP Proxy</Trans></h2>
|
|
||||||
<Input
|
|
||||||
theme="round-black"
|
|
||||||
placeholder={_i18n._(t`Type HTTP proxy url (example: http://proxy.acme.org:8080)`)}
|
|
||||||
value={this.httpProxy}
|
|
||||||
onChange={v => this.httpProxy = v}
|
|
||||||
onBlur={() => preferences.httpsProxy = this.httpProxy}
|
|
||||||
/>
|
|
||||||
<small className="hint">
|
|
||||||
<Trans>Proxy is used only for non-cluster communication.</Trans>
|
|
||||||
</small>
|
|
||||||
|
|
||||||
<h2><Trans>Certificate Trust</Trans></h2>
|
<h2><Trans>Certificate Trust</Trans></h2>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={<Trans>Allow untrusted Certificate Authorities</Trans>}
|
label={<Trans>Allow untrusted Certificate Authorities</Trans>}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import type { InputProps } from "./input";
|
|||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
import { _i18n } from '../../i18n';
|
import { _i18n } from '../../i18n';
|
||||||
|
import fse from "fs-extra";
|
||||||
|
|
||||||
export interface Validator {
|
export interface Validator {
|
||||||
debounce?: number; // debounce for async validators in ms
|
debounce?: number; // debounce for async validators in ms
|
||||||
@ -41,6 +42,12 @@ export const isUrl: Validator = {
|
|||||||
validate: value => !!value.match(/^http(s)?:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)*$/),
|
validate: value => !!value.match(/^http(s)?:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)*$/),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isPath: Validator = {
|
||||||
|
condition: ({ type }) => type === "text",
|
||||||
|
message: () => _i18n._(t`This field must be a path to an existing file`),
|
||||||
|
validate: value => !value || fse.pathExistsSync(value),
|
||||||
|
}
|
||||||
|
|
||||||
export const minLength: Validator = {
|
export const minLength: Validator = {
|
||||||
condition: ({ minLength }) => !!minLength,
|
condition: ({ minLength }) => !!minLength,
|
||||||
message: (value, { minLength }) => _i18n._(t`Minimum length is ${minLength}`),
|
message: (value, { minLength }) => _i18n._(t`Minimum length is ${minLength}`),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user