From 75825d06285edb7bd8e878ec3da1a8f02ac9b30e Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Thu, 3 Sep 2020 14:39:43 +0300 Subject: [PATCH] Adding kubectl binaries section in Preferences Signed-off-by: Alex Andreev --- src/common/user-store.ts | 16 +++++ .../+preferences/kubectl-binaries.tsx | 68 +++++++++++++++++++ .../components/+preferences/preferences.scss | 5 ++ .../components/+preferences/preferences.tsx | 3 + .../components/input/input.validators.ts | 7 ++ 5 files changed, 99 insertions(+) create mode 100644 src/renderer/components/+preferences/kubectl-binaries.tsx diff --git a/src/common/user-store.ts b/src/common/user-store.ts index 6f3e2605be..ef72fd72bf 100644 --- a/src/common/user-store.ts +++ b/src/common/user-store.ts @@ -1,4 +1,5 @@ import type { ThemeId } from "../renderer/theme.store"; +import { app, remote } from 'electron'; import semver from "semver" import { readFile } from "fs-extra" import { action, observable, reaction, toJS } from "mobx"; @@ -8,6 +9,7 @@ import { getAppVersion } from "./utils/app-version"; import { kubeConfigDefaultPath, loadConfig } from "./kube-helpers"; import { tracker } from "./tracker"; import logger from "../main/logger"; +import path from 'path'; export interface UserStoreModel { kubeConfigPath: string; @@ -22,6 +24,9 @@ export interface UserPreferences { allowUntrustedCAs?: boolean; allowTelemetry?: boolean; downloadMirror?: string | "default"; + downloadKubectlBinaries?: boolean; + downloadBinariesPath?: string; + kubectlBinariesPath?: string; } export class UserStore extends BaseStore { @@ -53,6 +58,9 @@ export class UserStore extends BaseStore { allowUntrustedCAs: false, colorTheme: UserStore.defaultTheme, downloadMirror: "default", + downloadKubectlBinaries: true, // Download kubectl binaries matching cluster version + downloadBinariesPath: "", + kubectlBinariesPath: "$PATH/kubectl" }; get isNewVersion() { @@ -98,6 +106,14 @@ export class UserStore extends BaseStore { this.newContexts.clear(); } + /** + * Getting default directory to download kubectl binaries + * @returns string + */ + getDefaultKubectlPath(): string { + return path.join((app || remote.app).getPath("userData"), "binaries") + } + @action protected async fromStore(data: Partial = {}) { const { lastSeenAppVersion, seenContexts = [], preferences, kubeConfigPath } = data diff --git a/src/renderer/components/+preferences/kubectl-binaries.tsx b/src/renderer/components/+preferences/kubectl-binaries.tsx new file mode 100644 index 0000000000..bbe55ed7b0 --- /dev/null +++ b/src/renderer/components/+preferences/kubectl-binaries.tsx @@ -0,0 +1,68 @@ +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'; + +export const KubectlBinaries = observer(({ preferences }: { preferences: UserPreferences }) => { + const [downloadPath, setDownloadPath] = useState(preferences.downloadBinariesPath || ""); + const [binariesPath, setBinariesPath] = useState(preferences.kubectlBinariesPath || ""); + + const save = () => { + preferences.downloadBinariesPath = downloadPath; + preferences.kubectlBinariesPath = binariesPath; + } + + const renderPath = () => { + if (preferences.downloadKubectlBinaries) { + return null; + } + return ( + <> + + + + Default: $PATH/kubectl + + + ); + } + + return ( + <> +

Kubectl Binary

+ + Download kubectl binaries matching to Kubernetes cluster verison. + + Download kubectl binaries} + value={preferences.downloadKubectlBinaries} + onChange={() => + preferences.downloadKubectlBinaries = !preferences.downloadKubectlBinaries + } + /> + + + Default: {userStore.getDefaultKubectlPath()} + + {renderPath()} + + ); +}); \ No newline at end of file diff --git a/src/renderer/components/+preferences/preferences.scss b/src/renderer/components/+preferences/preferences.scss index 33072a4849..62bd307cd1 100644 --- a/src/renderer/components/+preferences/preferences.scss +++ b/src/renderer/components/+preferences/preferences.scss @@ -19,6 +19,11 @@ } } + .SubTitle { + text-transform: none; + margin: 0!important; + } + .repos { position: relative; diff --git a/src/renderer/components/+preferences/preferences.tsx b/src/renderer/components/+preferences/preferences.tsx index f2e874750b..6546ec28de 100644 --- a/src/renderer/components/+preferences/preferences.tsx +++ b/src/renderer/components/+preferences/preferences.tsx @@ -16,6 +16,7 @@ import { Badge } from "../badge"; import { themeStore } from "../../theme.store"; import { history } from "../../navigation"; import { Tooltip } from "../tooltip"; +import { KubectlBinaries } from "./kubectl-binaries"; @observer export class Preferences extends React.Component { @@ -169,6 +170,8 @@ export class Preferences extends React.Component { Proxy is used only for non-cluster communication. + +

Certificate Trust

Allow untrusted Certificate Authorities} diff --git a/src/renderer/components/input/input.validators.ts b/src/renderer/components/input/input.validators.ts index 2236265e5d..799a610824 100644 --- a/src/renderer/components/input/input.validators.ts +++ b/src/renderer/components/input/input.validators.ts @@ -2,6 +2,7 @@ import type { InputProps } from "./input"; import { ReactNode } from "react"; import { t } from "@lingui/macro"; import { _i18n } from '../../i18n'; +import fse from "fs-extra"; export interface Validator { 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\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)*$/), }; +export const isPath: Validator = { + condition: ({ type }) => type === "text", + message: () => _i18n._(t`This field must be a path to an existing file`), + validate: value => fse.pathExistsSync(value), +} + export const minLength: Validator = { condition: ({ minLength }) => !!minLength, message: (value, { minLength }) => _i18n._(t`Minimum length is ${minLength}`),