project restructuring and initial refactoring

This commit is contained in:
Roman committed 2020-06-09 15:14:40 +03:00
1 parent 5af12bdbbd
commit aa0eca2dc4
807 files changed
+11848 -22947

No files matched your search

-4
View File
@@ -71,8 +71,6 @@ jobs:
condition: eq(variables.CACHE_RESTORED, 'true')
- script: make deps
displayName: Install dependencies
- script: make lint
displayName: Lint
- script: make test
displayName: Run tests
- script: make build
@@ -115,8 +113,6 @@ jobs:
condition: eq(variables.CACHE_RESTORED, 'true')
- script: make deps
displayName: Install dependencies
- script: make lint
displayName: Lint
- script: make test
displayName: Run tests
- bash: |
+1 -1
View File
@@ -1,7 +1,7 @@
{
"plugins": [
"macros",
"@babel/plugin-transform-runtime",
"@babel/plugin-transform-runtime"
],
"presets": [
"@babel/preset-env",
-76
View File
@@ -1,76 +0,0 @@
module.exports = {
overrides: [
{
files: [
"src/renderer/**/*.js",
"build/**/*.js",
"src/renderer/**/*.vue"
],
extends: [
'eslint:recommended',
'plugin:vue/recommended'
],
env: {
node: true
},
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
rules: {
"indent": ["error", 2],
"no-unused-vars": "off",
"vue/order-in-components": "off",
"vue/attributes-order": "off",
"vue/max-attributes-per-line": "off"
}
},
{
files: [
"build/*.ts",
"src/**/*.ts",
"spec/**/*.ts"
],
parser: "@typescript-eslint/parser",
extends: [
'plugin:@typescript-eslint/recommended',
],
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
rules: {
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": "off",
"indent": ["error", 2]
},
},
{
files: [
"dashboard/**/*.ts",
"dashboard/**/*.tsx",
],
parser: "@typescript-eslint/parser",
extends: [
'plugin:@typescript-eslint/recommended',
],
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
jsx: true,
},
rules: {
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/interface-name-prefix": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"indent": ["error", 2]
},
}
]
};
+1
View File
@@ -7,3 +7,4 @@ tmp/
static/build/client/
binaries/client/
binaries/server/
locales/**/**.js
+1 -1
View File
@@ -12,7 +12,7 @@
"catalogs": [
{
"path": "./locales/{locale}/messages",
"include": "./client"
"include": "./renderer"
}
]
}
+21 -30
View File
@@ -7,44 +7,35 @@ endif
.PHONY: dev build test clean
download-bins:
yarn download:bins
yarn download-bins
dev: app-deps dashboard-deps
yarn dev
test: test-app test-dashboard
lint:
yarn lint
test-app:
yarn test
deps: app-deps dashboard-deps
app-deps:
install-deps:
yarn install --frozen-lockfile
build: build-dashboard app-deps
yarn install
dev: install-deps
yarn dev
test:
yarn test
integration-linux:
yarn build:linux
yarn integration
integration-mac:
yarn build:mac
yarn integration
integration-win:
yarn build:win
yarn integration
build: install-deps
ifeq "$(DETECTED_OS)" "Windows"
yarn dist:win
else
yarn dist
endif
dashboard-deps:
cd dashboard && yarn install --frozen-lockfile
clean-dashboard:
rm -rf dashboard/build/ && rm -rf static/build/client
test-dashboard: dashboard-deps
cd dashboard && yarn test
build-dashboard: dashboard-deps clean-dashboard
export NODE_ENV=production
cd dashboard && yarn build
clean:
rm -rf dist/*
-14
View File
@@ -1,14 +0,0 @@
module.exports = {
require: jest.fn(),
match: jest.fn(),
app: {
getVersion: jest.fn().mockReturnValue("3.0.0"),
getPath: jest.fn().mockReturnValue("/foo/bar")
},
remote: {
app: {
getPath: jest.fn()
}
},
dialog: jest.fn()
};
+1 -1
View File
@@ -1,3 +1,3 @@
import { helmCli } from "../src/main/helm-cli"
import { helmCli } from "../main/helm-cli"
helmCli.ensureBinary()
+7 -6
View File
@@ -1,9 +1,10 @@
import * as request from "request"
import * as fs from "fs"
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 * as md5File from "md5-file"
import * as requestPromise from "request-promise-native"
import * as path from "path"
import path from "path"
class KubectlDownloader {
public kubectlVersion: string
@@ -86,7 +87,7 @@ class KubectlDownloader {
}
}
const downloadVersion: string = require("../package.json").config.bundledKubectlVersion
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') },
File renamed without changes.
@@ -1,12 +1,11 @@
import * as ElectronStore from "electron-store"
import ElectronStore from "electron-store"
import { Cluster, ClusterBaseInfo } from "../main/cluster";
import { getAppVersion } from "./app-utils"
import * as version200Beta2 from "./migrations/cluster-store/2.0.0-beta.2"
import * as version241 from "./migrations/cluster-store/2.4.1"
import * as version260Beta2 from "./migrations/cluster-store/2.6.0-beta.2"
import * as version260Beta3 from "./migrations/cluster-store/2.6.0-beta.3"
import * as version270Beta0 from "./migrations/cluster-store/2.7.0-beta.0"
import * as version270Beta1 from "./migrations/cluster-store/2.7.0-beta.1"
import * as version200Beta2 from "../migrations/cluster-store/2.0.0-beta.2"
import * as version241 from "../migrations/cluster-store/2.4.1"
import * as version260Beta2 from "../migrations/cluster-store/2.6.0-beta.2"
import * as version260Beta3 from "../migrations/cluster-store/2.6.0-beta.3"
import * as version270Beta0 from "../migrations/cluster-store/2.7.0-beta.0"
import * as version270Beta1 from "../migrations/cluster-store/2.7.0-beta.1"
export class ClusterStore {
private static instance: ClusterStore;
@@ -15,7 +14,6 @@ export class ClusterStore {
private constructor() {
this.store = new ElectronStore({
name: "lens-cluster-store",
projectVersion: getAppVersion(),
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
migrations: {
"2.0.0-beta.2": version200Beta2.migration,
@@ -58,7 +56,9 @@ export class ClusterStore {
public getCluster(id: string): Cluster {
const cluster = this.getAllClusterObjects().find((cluster) => cluster.id === id)
if (cluster) { return cluster}
if (cluster) {
return cluster
}
return null
}
@@ -74,7 +74,8 @@ export class ClusterStore {
}
if (index === -1) {
clusters.push(storable)
} else {
}
else {
clusters[index] = storable
}
this.store.set("clusters", clusters)
@@ -97,7 +98,7 @@ export class ClusterStore {
}
static getInstance(): ClusterStore {
if(!ClusterStore.instance) {
if (!ClusterStore.instance) {
ClusterStore.instance = new ClusterStore();
}
return ClusterStore.instance;
@@ -108,6 +109,4 @@ export class ClusterStore {
}
}
const clusterStore: ClusterStore = ClusterStore.getInstance();
export { clusterStore };
export const clusterStore = ClusterStore.getInstance();
File renamed without changes.
+1 -1
View File
@@ -1,4 +1,4 @@
import * as request from "request"
import request from "request"
import { userStore } from "../common/user-store"
export function globalRequestOpts(requestOpts: request.Options ) {
@@ -1,6 +1,7 @@
import * as winca from "win-ca/api"
import "mac-ca"
import winca from "win-ca/api"
import { isWindows } from "./vars";
if (process.platform === "win32") {
if (isWindows) {
winca.inject("+") // see: https://github.com/ukoloff/win-ca#caveats
}
+2 -2
View File
@@ -1,6 +1,6 @@
import { machineIdSync } from 'node-machine-id'
import ua from "universal-analytics"
import { machineIdSync } from "node-machine-id"
import { userStore } from "../common/user-store"
import * as ua from "universal-analytics"
const GA_ID = "UA-159377374-1"
@@ -1,6 +1,5 @@
import * as ElectronStore from "electron-store"
import * as appUtil from "./app-utils"
import * as version210Beta4 from "./migrations/user-store/2.1.0-beta.4"
import ElectronStore from "electron-store"
import * as version210Beta4 from "../migrations/user-store/2.1.0-beta.4"
export interface User {
id?: string;
@@ -20,7 +19,6 @@ export class UserStore {
private constructor() {
this.store = new ElectronStore({
projectVersion: appUtil.getAppVersion(),
migrations: {
"2.1.0-beta.4": version210Beta4.migration,
}
@@ -68,7 +66,7 @@ export class UserStore {
}
static getInstance(): UserStore {
if(!UserStore.instance) {
if (!UserStore.instance) {
UserStore.instance = new UserStore();
}
return UserStore.instance;
+9
View File
@@ -0,0 +1,9 @@
import { app, remote } from "electron"
/**
*
* @returns app version correctly regardless of dev/prod mode and main/renderer differences
*/
export function getAppVersion(): string {
return (app || remote.app).getVersion();
}
File renamed without changes.
+18
View File
@@ -0,0 +1,18 @@
// Convert object's keys to camelCase format
import { camelCase, isPlainObject } from "lodash";
export function toCamelCase(obj: Record<string, any>): any {
if (Array.isArray(obj)) {
return obj.map(toCamelCase);
}
else if (isPlainObject(obj)) {
return Object.keys(obj).reduce((result, key) => {
const value = obj[key];
result[camelCase(key)] = typeof value === "object" ? toCamelCase(value) : value;
return result;
}, {} as any);
}
else {
return obj;
}
}
+5
View File
@@ -0,0 +1,5 @@
// Common utils bundle
export * from "./app-version"
export * from "./base64"
export * from "./camelCase"
+24
View File
@@ -0,0 +1,24 @@
// App's common paths/flags/etc. for any process
import packageInfo from "../package.json"
import path from "path";
const { main, renderer } = packageInfo.electronWebpack;
export const outDir = path.resolve(__dirname, "../dist");
export const mainDir = path.resolve(__dirname, "../", main.sourceDirectory);
export const rendererDir = path.resolve(__dirname, "../", renderer.sourceDirectory);
export const isMac = process.platform === "darwin"
export const isWindows = process.platform === "win32"
export const isProduction = process.env.NODE_ENV === "production"
export const isDevelopment = !isProduction;
export const buildVersion = process.env.BUILD_VERSION;
export const apiPrefix = {
BASE: '/api',
TERMINAL: '/api-terminal', // terminal api
KUBE_BASE: '/api-kube', // kubernetes cluster api
KUBE_USERS: '/api-users', // users & groups api
KUBE_HELM: '/api-helm', // helm charts api
KUBE_RESOURCE_APPLIER: "/api-resource",
};
@@ -1,4 +1,4 @@
import * as ElectronStore from "electron-store"
import ElectronStore from "electron-store"
import { clusterStore } from "./cluster-store"
export interface WorkspaceData {
-12
View File
@@ -1,12 +0,0 @@
.idea
node_modules/
build/
dist/
wireframes/
backup
npm-debug.log
.vscode
.env
/tslint.json
*.DS_Store
docker-compose.yml
-13
View File
@@ -1,13 +0,0 @@
.idea
node_modules
build/
dist/
wireframes/
backup
npm-debug.log
.vscode
dump.rdb
*.env
/tslint.json
*.DS_Store
locales/_build/
@@ -1,47 +0,0 @@
import { CronJob } from "../";
//jest.mock('../../../components/+login/auth.store.ts', () => 'authStore');
jest.mock('../../kube-watch-api.ts', () => 'kube-watch-api');
const cronJob = new CronJob({
metadata: {
name: "hello",
namespace: "default",
selfLink: "/apis/batch/v1beta1/namespaces/default/cronjobs/hello",
uid: "cd3af13f-0b70-11ea-93da-9600002795a0",
resourceVersion: "51394448",
creationTimestamp: "2019-11-20T08:36:09Z",
},
spec: {
schedule: "30 06 31 12 *",
concurrencyPolicy: "Allow",
suspend: false,
},
status: {}
} as any)
describe("Check for CronJob schedule never run", () => {
test("Should be false with normal schedule", () => {
expect(cronJob.isNeverRun()).toBeFalsy();
});
test("Should be false with other normal schedule", () => {
cronJob.spec.schedule = "0 1 * * *";
expect(cronJob.isNeverRun()).toBeFalsy();
});
test("Should be true with date 31 of February", () => {
cronJob.spec.schedule = "30 06 31 2 *"
expect(cronJob.isNeverRun()).toBeTruthy();
});
test("Should be true with date 32 of July", () => {
cronJob.spec.schedule = "0 30 06 32 7 *"
expect(cronJob.isNeverRun()).toBeTruthy();
});
test("Should be false with predefined schedule", () => {
cronJob.spec.schedule = "@hourly";
expect(cronJob.isNeverRun()).toBeFalsy();
});
});
@@ -1,9 +0,0 @@
// App configuration api
import { apiBase } from "../index";
import { IConfig } from "../../../server/common/config";
export const configApi = {
getConfig() {
return apiBase.get<IConfig>("/config")
},
};
-117
View File
@@ -1,117 +0,0 @@
// Custom fonts, bundled with app
// Material Design Icons
// https://material.io/resources/icons/
// https://github.com/google/material-design-icons/tree/master/iconfont
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: local('Material Icons'), local('MaterialIcons-Regular'),
url("fonts/MaterialIcons-Regular.woff2") format("woff2");
}
// Google fonts
// https://fonts.google.com/
// Download & generate styles:
// https://google-webfonts-helper.herokuapp.com/fonts/roboto?subsets=latin,cyrillic
/* roboto-100 - cyrillic_latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'),
url('fonts/roboto-v20-cyrillic_latin-100.woff2') format('woff2');
}
/* roboto-100italic - cyrillic_latin */
@font-face {
font-family: 'Roboto';
font-style: italic;
font-weight: 100;
src: local('Roboto Thin Italic'), local('Roboto-ThinItalic'),
url('fonts/roboto-v20-cyrillic_latin-100italic.woff2') format('woff2');
}
/* roboto-300 - cyrillic_latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'),
url('fonts/roboto-v20-cyrillic_latin-300.woff2') format('woff2');
}
/* roboto-300italic - cyrillic_latin */
@font-face {
font-family: 'Roboto';
font-style: italic;
font-weight: 300;
src: local('Roboto Light Italic'), local('Roboto-LightItalic'),
url('fonts/roboto-v20-cyrillic_latin-300italic.woff2') format('woff2');
}
/* roboto-regular - cyrillic_latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'),
url('fonts/roboto-v20-cyrillic_latin-regular.woff2') format('woff2');
}
/* roboto-italic - cyrillic_latin */
@font-face {
font-family: 'Roboto';
font-style: italic;
font-weight: 400;
src: local('Roboto Italic'), local('Roboto-Italic'),
url('fonts/roboto-v20-cyrillic_latin-italic.woff2') format('woff2');
}
/* roboto-500 - cyrillic_latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'),
url('fonts/roboto-v20-cyrillic_latin-500.woff2') format('woff2');
}
/* roboto-500italic - cyrillic_latin */
@font-face {
font-family: 'Roboto';
font-style: italic;
font-weight: 500;
src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'),
url('fonts/roboto-v20-cyrillic_latin-500italic.woff2') format('woff2');
}
/* roboto-700 - cyrillic_latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'),
url('fonts/roboto-v20-cyrillic_latin-700.woff2') format('woff2');
}
/* roboto-700italic - cyrillic_latin */
@font-face {
font-family: 'Roboto';
font-style: italic;
font-weight: 700;
src: local('Roboto Bold Italic'), local('Roboto-BoldItalic'),
url('fonts/roboto-v20-cyrillic_latin-700italic.woff2') format('woff2');
}
// Patched Roboto Mono font with icons
// https://github.com/ryanoasis/nerd-fonts/tree/master/patched-fonts/RobotoMono
/* RobotoMono Windows Compatible for using in terminal */
@font-face {
font-family: 'RobotoMono';
src: local('RobotoMono'),
url('fonts/roboto-mono-nerd.ttf') format('truetype');
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

@@ -1 +0,0 @@
<svg enable-background="new 0 0 1034.7 1034.7" viewBox="0 0 1034.7 1034.7" xmlns="http://www.w3.org/2000/svg"><path d="m744 451.8v261.6l-75.5 43.3v-87.2l-75.5-43.3 75.5-43.3v-174.4l75.5 43.3v-261.6l-226.7-130.5-226.6 130.5v261.6l226.6-130.5 151.1 87.2-75.5 43.3v87.2l-151.1 87.2v-174.4l-75.5 43.3v261.6l-75.5-43.3v-261.6l-226.7 130.5v261.6l226.6 130.5 226.6-130.5-75.5-43.3v-87.2l75.5-43.3 75.5 43.3v87.2l-75.5 43.9 226.7 130.5 226.6-130.5v-261.6z"/></svg>

Before

Width:  |  Height:  |  Size: 456 B

-30
View File
@@ -1,30 +0,0 @@
{
"compilerOptions": {
"baseUrl": "../",
"rootDir": "../",
"outDir": "../build",
"jsx": "preserve",
"target": "es2016",
"lib": ["esnext", "dom", "dom.iterable"],
"module": "esnext",
"moduleResolution": "node",
"sourceMap": true,
"noImplicitAny": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"skipLibCheck": true,
"allowJs": true,
"allowSyntheticDefaultImports": true,
"traceResolution": false,
"resolveJsonModule": true,
"paths": {
"@lingui/macro": [
"node_modules/@types/lingui__macro"
]
}
},
"exclude": [
"../test",
"**/__tests__"
]
}
@@ -1,22 +0,0 @@
import { cpuUnitsToNumber } from "../convertCpu";
jest.mock('../../api/index', () => 'apiKube');
jest.mock('../../config.store', () => 'configStore');
describe("k8s CPU units conversion", () => {
test("Convert normal, nano(n), micro(u), milli(m) units to cores number", () => {
const units = [
"0.5",
"100m", // 0.1
"930000n", // 0.00093
"3028u", // 0.003028
]
const cpuCores = units.map(unit => cpuUnitsToNumber(unit))
const expected = [
0.5,
0.1,
0.00093,
0.003028
]
expect(cpuCores).toEqual(expected)
});
});
@@ -1,89 +0,0 @@
import { bytesToUnits, unitsToBytes } from "../convertMemory";
jest.mock('../../api/index', () => 'apiKube');
jest.mock('../../config.store', () => 'configStore');
describe("Kubernetes units conversion", () => {
test("Convert bytes to units", () => {
const bytes = [
128,
2048, // 2Ki
2097152, // 2Mi
4596968000, // 4.2Gi
4596968000000, // 4.1Ti
1.2384898975269E+15 // 1.1Pi
]
const units = bytes.map(byte => bytesToUnits(byte))
const expected = [
"128B",
"2.0Ki",
"2.0Mi",
"4.3Gi",
"4.2Ti",
"1.1Pi"
]
expect(units).toEqual(expected)
});
test("Convert bytes to units with decimal precision", () => {
const bytes = [
2107152, // 2.010Mi
4596968000, // 4.281Gi
]
const units = bytes.map(byte => bytesToUnits(byte, 3))
const expected = [
"2.010Mi",
"4.281Gi"
]
expect(units).toEqual(expected)
})
test("Convert 0 to bytes", () => {
expect(bytesToUnits(0)).toEqual("N/A");
});
test("Convert full units to bytes", () => {
const units = [
"128",
"22Ki", // 22528
"17.2Mi", // 18035507
"7.99Gi", // 8579197173
"2Ti", // 2199023255552
"1Pi", // 1125899906842624
]
const expected = [
128,
22528,
18035507,
8579197173,
2199023255552,
1125899906842624
]
const bytes = units.map(unitsToBytes)
expect(bytes).toEqual(expected)
});
test("Convert shorten units to bytes", () => {
const units = [
"128",
"22K", // 22528
"17.2M", // 18035507
"7.99G", // 8579197173
"2T", // 2199023255552
"1P", // 1125899906842624
]
const expected = [
128,
22528,
18035507,
8579197173,
2199023255552,
1125899906842624
]
const bytes = units.map(unitsToBytes)
expect(bytes).toEqual(expected)
});
test("Convert strange unit to bytes", () => {
expect(unitsToBytes("sss")).toEqual(NaN);
});
});
-18
View File
@@ -1,18 +0,0 @@
// Convert object's keys to camelCase format
import { camelCase, isPlainObject } from "lodash";
export function toCamelCase(data: any): any {
if (Array.isArray(data)) {
return data.map(toCamelCase);
}
else if (isPlainObject(data)) {
return Object.keys(data).reduce<any>((result, key) => {
const value = data[key];
result[camelCase(key)] = typeof value === "object" ? toCamelCase(value) : value;
return result;
}, {});
}
else {
return data;
}
}
-20
View File
@@ -1,20 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Lens - The Kubernetes IDE</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="apple-touch-icon" sizes="180x180" href="<%= require('./client/favicon/apple-touch-icon.png') %>">
<link rel="icon" type="image/png" sizes="32x32" href="<%= require('./client/favicon/favicon-32x32.png') %>">
<link rel="icon" type="image/png" sizes="16x16" href="<%= require('./client/favicon/favicon-16x16.png') %>">
<link rel="icon" type="image/png" sizes="512x512" href="<%= require('./client/favicon/android-chrome-512x512.png') %>">
<link rel="mask-icon" color="#5bbad5" href="<%= require('./client/favicon/safari-pinned-tab.svg') %>">
<link rel="preload" as="font" href="<%= require('./client/components/fonts/roboto-mono-nerd.ttf') %>" type="font/ttf" crossorigin>
<link rel="preload" as="font" href="<%= require('./client/components/fonts/MaterialIcons-Regular.woff2') %>" type="font/woff2" crossorigin>
</head>
<body>
<div id="app"></div>
</body>
</html>
-1
View File
@@ -1 +0,0 @@
/* eslint-disable */module.exports={localeData:{"plurals":function(n,ord){var s=String(n).split("."),v0=!s[1],t0=Number(s[0])==n,n10=t0&&s[0].slice(-1),n100=t0&&s[0].slice(-2);if(ord)return n10==1&&n100!=11?"one":n10==2&&n100!=12?"two":n10==3&&n100!=13?"few":"other";return n==1&&v0?"one":"other"}},messages:{"(as a percentage of request)":"(as a percentage of request)","(empty) (Allowing the specific traffic to all pods in this namespace)":"(empty) (Allowing the specific traffic to all pods in this namespace)","<0>Filtered</0>: {itemsCount} / {allItemsCount}":["<0>Filtered</0>: ",["itemsCount"]," / ",["allItemsCount"]],"<0>Your browser does not support all Lens features. </0> Please consider using another browser.":"<0>Your browser does not support all Lens features. </0> Please consider using another browser.","<0>{0}</0> successfully created":["<0>",["0"],"</0> successfully created"],"API Group":"API Group","Access Control":"Access Control","Access Modes":"Access Modes","Account Name":"Account Name","Active":"Active","Add Namespace":"Add Namespace","Add RoleBinding":"Add RoleBinding","Add bindings to {name}":["Add bindings to ",["name"]],"Add field":"Add field","Additional Printer Columns":"Additional Printer Columns","Addresses":"Addresses","Affinities":"Affinities","Age":"Age","All groups":"All groups","All logs":"All logs","All namespaces":"All namespaces","Allocatable":"Allocatable","Allow Privilege Escalation":"Allow Privilege Escalation","Allowed CSI Drivers":"Allowed CSI Drivers","Allowed Capabilities":"Allowed Capabilities","Allowed Flex Volumes":"Allowed Flex Volumes","Allowed Host Paths":"Allowed Host Paths","Allowed Proc Mount Types":"Allowed Proc Mount Types","Allowed Runtime Class Names":"Allowed Runtime Class Names","Allowed Unsafe Sysctls":"Allowed Unsafe Sysctls","Annotations":"Annotations","Api Groups":"Api Groups","App Version":"App Version","App crash at <0>{pageUrl}</0>":["App crash at <0>",["pageUrl"],"</0>"],"Applying..":"Applying..","Apps":"Apps","Are you sure you want to drain <0>{nodeName}</0>?":["Are you sure you want to drain <0>",["nodeName"],"</0>?"],"Arguments":"Arguments","Auth App Role":"Auth App Role","Back":"Back","Backends":"Backends","Binding":"Binding","Binding targets":"Binding targets","Bindings":"Bindings","Build version":"Build version","Bytes consumed on this filesystem":"Bytes consumed on this filesystem","Bytes received by all containers":"Bytes received by all containers","Bytes sent by Ingress controller with error status":"Bytes sent by Ingress controller with error status","Bytes sent by Ingress controller with successful status":"Bytes sent by Ingress controller with successful status","Bytes sent, status 2xx":"Bytes sent, status 2xx","Bytes sent, status 5xx":"Bytes sent, status 5xx","Bytes transmitted from all containers":"Bytes transmitted from all containers","CA Bundle":"CA Bundle","CPU":"CPU","CPU capacity":"CPU capacity","CPU cores usage":"CPU cores usage","CPU limits":"CPU limits","CPU requests":"CPU requests","CPU:":"CPU:","Cancel":"Cancel","Capacity":"Capacity","Certificates":"Certificates","Chart":"Chart","Chart Release <0>{0}</0> successfully created.":["Chart Release <0>",["0"],"</0> successfully created."],"Chart: {0}":["Chart: ",["0"]],"Charts":"Charts","Checking update":"Checking update","Claim":"Claim","Claim Name":"Claim Name","Close":"Close","Close (Ctrl+W)":"Close (Ctrl+W)","Cloud API Token Secret":"Cloud API Token Secret","Cluster":"Cluster","Cluster IP":"Cluster IP","Cluster Issuers":"Cluster Issuers","Command":"Command","Common Name":"Common Name","Compact view":"Compact view","Completions":"Completions","Component stack":"Component stack","Condition":"Condition","Conditions":"Conditions","Config Maps":"Config Maps","Config copied to clipboard":"Config copied to clipboard","ConfigMap <0>{0}</0> successfully updated.":["ConfigMap <0>",["0"],"</0> successfully updated."],"ConfigMaps":"ConfigMaps","Configuration":"Configuration","Connection":"Connection","Container":"Container","Container CPU cores usage":"Container CPU cores usage","Container CPU requests":"Container CPU requests","Container memory limits":"Container memory limits","Container memory requests":"Container memory requests","Container memory usage":"Container memory usage","Container runtime":"Container runtime","Containers":"Containers","Context":"Context","Controlled By":"Controlled By","Controlled by":"Controlled by","Conversion":"Conversion","Copy to clipboard":"Copy to clipboard","Cordon":"Cordon","Count":"Count","Create":"Create","Create Namespace":"Create Namespace","Create ResourceQuota":"Create ResourceQuota","Create Role":"Create Role","Create Secret":"Create Secret","Create Service Account":"Create Service Account","Create new ResourceQuota":"Create new ResourceQuota","Create new Role":"Create new Role","Create new RoleBinding":"Create new RoleBinding","Create new Secret":"Create new Secret","Create new Service Account":"Create new Service Account","Create resourcLine truncated
File diff suppressed because it is too large. Load diff
-1
View File
@@ -1 +0,0 @@
/* eslint-disable */module.exports={localeData:{"plurals":function(n,ord){var s=String(n).split("."),i=s[0],v0=!s[1],i10=i.slice(-1),i100=i.slice(-2);if(ord)return"other";return v0&&i10==1&&i100!=11?"one":v0&&i10>=2&&i10<=4&&(i100<12||i100>14)?"few":v0&&i10==0||v0&&i10>=5&&i10<=9||v0&&i100>=11&&i100<=14?"many":"other"}},messages:{"(as a percentage of request)":"(as a percentage of request)","(empty) (Allowing the specific traffic to all pods in this namespace)":"(\u041F\u0443\u0441\u0442\u043E) (\u0414\u043E\u043F\u0443\u0441\u043A\u0430\u0435\u0442\u0441\u044F \u0442\u0440\u0430\u0444\u0438\u043A \u043A\u043E \u0432\u0441\u0435\u043C \u043F\u043E\u0434\u0430\u043C \u0432 \u0434\u0430\u043D\u043D\u043E\u0439 \u043E\u0431\u043B\u0430\u0441\u0442\u0438 \u0438\u043C\u0435\u043D)","<0>Filtered</0>: {itemsCount} / {allItemsCount}":["<0>\u041E\u0442\u0444\u0438\u043B\u044C\u0442\u0440\u043E\u0432\u0430\u043D\u043E</0>: ",["itemsCount"]," / ",["allItemsCount"]],"<0>Your browser does not support all Lens features. </0> Please consider using another browser.":"<0>\u0412\u0430\u0448 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E\u0441\u0442\u0438 Lens. </0> \u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430 \u0440\u0430\u0441\u0441\u043C\u043E\u0442\u0440\u0438\u0442\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435 \u0434\u0440\u0443\u0433\u043E\u0433\u043E \u0441\u043E\u0432\u0440\u0435\u043C\u0435\u043D\u043D\u043E\u0433\u043E \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430.","<0>{0}</0> successfully created":["<0>",["0"],"</0> successfully created"],"API Group":"API Group","Access Control":"\u041A\u043E\u043D\u0442\u0440\u043E\u043B\u044C \u0434\u043E\u0441\u0442\u0443\u043F\u0430","Access Modes":"\u0420\u0435\u0436\u0438\u043C\u044B \u0434\u043E\u0441\u0442\u0443\u043F\u0430","Account Name":"\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0430\u043A\u043A\u0430\u0443\u043D\u0442\u0430","Active":"\u0410\u043A\u0442\u0438\u0432\u043D\u044B\u0439","Add Namespace":"\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C Namespace","Add RoleBinding":"\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043F\u0440\u0438\u0432\u044F\u0437\u043A\u0443 \u0440\u043E\u043B\u0435\u0439","Add bindings to {name}":["\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043F\u0440\u0438\u0432\u044F\u0437\u043A\u0438 \u043A ",["name"]],"Add field":"\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043F\u043E\u043B\u0435","Additional Printer Columns":"Additional Printer Columns","Addresses":"\u0410\u0434\u0440\u0435\u0441\u0430","Affinities":"\u0410\u0444\u0444\u0438\u043D\u0438\u0442\u0435\u0442\u044B","Age":"\u0412\u043E\u0437\u0440\u0430\u0441\u0442","All groups":"All groups","All logs":"\u0412\u0441\u0435 \u043B\u043E\u0433\u0438","All namespaces":"All namespaces","Allocatable":"Allocatable","Allow Privilege Escalation":"Allow Privilege Escalation","Allowed CSI Drivers":"Allowed CSI Drivers","Allowed Capabilities":"Allowed Capabilities","Allowed Flex Volumes":"Allowed Flex Volumes","Allowed Host Paths":"Allowed Host Paths","Allowed Proc Mount Types":"Allowed Proc Mount Types","Allowed Runtime Class Names":"Allowed Runtime Class Names","Allowed Unsafe Sysctls":"Allowed Unsafe Sysctls","Annotations":"\u0410\u043D\u043D\u043E\u0442\u0430\u0446\u0438\u0438","Api Groups":"API \u0433\u0440\u0443\u043F\u043F\u044B","App Version":"\u0412\u0435\u0440\u0441\u0438\u044F \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F","App crash at <0>{pageUrl}</0>":["\u0421\u0431\u043E\u0439 \u0440\u0430\u0431\u043E\u0442\u044B \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u043D\u0430 <0>",["pageUrl"],"</0>"],"Applying..":"\u041F\u0440\u0438\u043C\u0435\u043D\u0435\u043D\u0438\u0435..","Apps":"\u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F","Are you sure you want to drain <0>{nodeName}</0>?":["\u0412\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u043A\u043E\u043C\u0430\u043D\u0434\u0443 drain \u0434\u043B\u044F \u043D\u043E\u0434\u044B <0>",["nodeName"],"</0>?"],"Arguments":"\u0410\u0440\u0433\u0443\u043C\u0435\u043D\u0442\u044B","Auth App Role":"Auth App Role","Back":"\u041D\u0430\u0437\u0430\u0434","Backends":"\u0411\u044D\u043A\u0435\u043D\u0434\u044B","Binding":"\u041F\u0440\u0438\u0432\u044F\u0437\u043A\u0430","Binding targets":"\u0426\u0435\u043B\u0438 \u043F\u0440\u0438\u0432\u044F\u0437\u043A\u0438","Bindings":"\u041F\u0440\u0438\u0432\u044F\u0437\u043A\u0438","Build version":"\u0412\u0435\u0440\u0441\u0438\u044F \u0431\u0438\u043B\u0434\u0430","Bytes consumed on this filesystem":"\u0411\u0430\u0439\u0442\u044B, \u043F\u043E\u0442\u0440\u0435\u0431\u043B\u044F\u0435\u043C\u044B\u0435 \u0432 \u0444\u0430\u0439\u043B\u043E\u0432\u043E\u0439 \u0441\u0438\u0441\u0442\u0435\u043C\u0435","Bytes received by all containerLine truncated
File diff suppressed because it is too large. Load diff
-129
View File
@@ -1,129 +0,0 @@
{
"name": "lens-app-dashboard",
"version": "0.0.0",
"scripts": {
"dev": "webpack-cli --watch --cache --progress --output-path ../static/build/client/",
"build": "webpack -p --progress --output-path ../static/build/client/",
"test": "jest --config './test/jest.config.js'",
"add-locale": "lingui add-locale",
"lingui-extract": "lingui extract --clean",
"lingui-compile": "lingui compile"
},
"dependencies": {
"axios": "^0.19.0",
"chalk": "^2.4.2",
"compare-versions": "^3.6.0",
"compression": "^1.7.4",
"cookie-session": "^1.3.3",
"cors": "^2.8.5",
"crypto-js": "^3.1.9-1",
"dotenv": "^8.2.0",
"ip": "^1.1.5",
"js-yaml": "^3.13.1",
"jsonpath": "^1.0.2",
"lodash": "^4.17.15",
"morgan": "^1.9.1"
},
"devDependencies": {
"@babel/core": "^7.7.2",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-proposal-decorators": "^7.8.3",
"@babel/plugin-proposal-object-rest-spread": "^7.8.3",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/plugin-transform-runtime": "^7.6.2",
"@babel/preset-env": "=7.9.0",
"@babel/preset-react": "^7.7.0",
"@babel/preset-typescript": "^7.8.3",
"@babel/runtime": "^7.7.2",
"@lingui/cli": "^3.0.0-7",
"@lingui/loader": "^3.0.0-7",
"@lingui/macro": "^3.0.0-7",
"@lingui/react": "^3.0.0-7",
"@material-ui/core": "^4.6.0",
"@types/chart.js": "^2.9.1",
"@types/color": "^3.0.0",
"@types/compression": "^1.0.1",
"@types/cookie-session": "^2.0.37",
"@types/cors": "^2.8.6",
"@types/crypto-js": "^3.1.43",
"@types/dompurify": "^2.0.0",
"@types/dotenv": "^8.2.0",
"@types/enzyme": "^3.10.3",
"@types/enzyme-adapter-react-16": "^1.0.5",
"@types/express": "^4.17.2",
"@types/helmet": "^0.0.45",
"@types/history": "^4.7.3",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/html-webpack-plugin": "^3.2.1",
"@types/http-proxy-middleware": "^0.19.3",
"@types/ip": "^1.1.0",
"@types/jest": "^24.0.22",
"@types/js-yaml": "^3.12.1",
"@types/jsonpath": "^0.2.0",
"@types/lingui__macro": "^2.7.3",
"@types/lodash": "^4.14.146",
"@types/marked": "^0.7.0",
"@types/material-ui": "^0.21.7",
"@types/mini-css-extract-plugin": "^0.8.0",
"@types/morgan": "^1.7.37",
"@types/node": "^12.12.7",
"@types/react": "^16.9.11",
"@types/react-dom": "^16.9.4",
"@types/react-router-dom": "^5.1.2",
"@types/react-select": "^3.0.8",
"@types/react-window": "^1.8.1",
"@types/terser-webpack-plugin": "^2.2.0",
"@types/webpack": "^4.39.8",
"ace-builds": "^1.4.7",
"ansi_up": "^4.0.4",
"babel-core": "^7.0.0-bridge.0",
"babel-loader": "^8.0.6",
"babel-plugin-macros": "^2.6.1",
"chart.js": "^2.9.2",
"color": "^3.1.2",
"commander": "^4.0.1",
"concurrently": "^5.1.0",
"css-element-queries": "^1.2.1",
"css-loader": "^3.2.0",
"dompurify": "^2.0.7",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.15.1",
"file-loader": "^4.2.0",
"flex.box": "^3.4.4",
"fs-extra": "^8.1.0",
"hoist-non-react-statics": "^3.3.0",
"html-webpack-plugin": "3.2.0",
"identity-obj-proxy": "^3.0.0",
"include-media": "^1.4.9",
"jest": "^24.9.0",
"marked": "^0.7.0",
"mini-css-extract-plugin": "^0.8.0",
"mobx": "^5.15.0",
"mobx-observable-history": "^1.0.0",
"mobx-react": "^6.1.4",
"moment": "^2.24.0",
"node-sass": "^4.13.0",
"nodemon": "^1.19.4",
"path-to-regexp": "^3.2.0",
"pkg": "^4.4.4",
"raw-loader": "^3.1.0",
"react": "^16.11.0",
"react-dom": "^16.11.0",
"react-router-dom": "^5.1.2",
"react-select": "^3.0.8",
"react-window": "^1.8.5",
"sass-loader": "^8.0.0",
"style-loader": "^1.0.0",
"ts-jest": "^24.1.0",
"ts-loader": "^6.2.1",
"ts-node": "^8.5.0",
"typescript": "^3.7.2",
"url-loader": "^2.2.0",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0",
"xterm": "^4.4.0-vscode1",
"xterm-addon-fit": "^0.3.0",
"yargs": "^14.2.0"
}
}
-4
View File
@@ -1,4 +0,0 @@
export interface IClusterInfo {
kubeVersion?: string;
clusterName?: string;
}
-12
View File
@@ -1,12 +0,0 @@
import { IClusterInfo } from "../common/cluster";
export interface IConfig extends Partial<IClusterInfo> {
lensVersion?: string;
lensTheme?: string;
username?: string;
token?: string;
allowedNamespaces?: string[];
allowedResources?: string[];
isClusterAdmin?: boolean;
chartsEnabled: boolean;
kubectlAccess?: boolean; // User accessed via kubectl-lens plugin
}
-14
View File
@@ -1,14 +0,0 @@
export interface IKubeWatchEvent<T = any> {
type: "ADDED" | "MODIFIED" | "DELETED";
object?: T;
}
export interface IKubeWatchRouteEvent {
type: "STREAM_END";
url: string;
status: number;
}
export interface IKubeWatchRouteQuery {
api: string | string[];
}
-4
View File
@@ -1,4 +0,0 @@
export type IMetricsQuery = string | string[] | {
[metricName: string]: string | object;
}
-74
View File
@@ -1,74 +0,0 @@
// Server-side config
export const CLIENT_DIR = "client";
export const BUILD_DIR = "build";
export const IS_PRODUCTION = process.env.NODE_ENV === "production";
export const KUBERNETES_SERVICE_HOST = process.env.KUBERNETES_SERVICE_HOST || "kubernetes";
export const KUBERNETES_SERVICE_PORT = Number(process.env.KUBERNETES_SERVICE_PORT || 443);
export const KUBERNETES_SERVICE_URL = `https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}`;
export const config = {
IS_PRODUCTION: IS_PRODUCTION,
LENS_VERSION: process.env.LENS_VERSION,
LENS_THEME: process.env.LENS_THEME,
BUILD_VERSION: process.env.BUILD_VERSION,
API_PREFIX: {
BASE: '/api', // local express.js server api
TERMINAL: '/api-terminal', // terminal api
KUBE_BASE: '/api-kube', // kubernetes cluster api
KUBE_USERS: '/api-users', // users & groups api
KUBE_HELM: '/api-helm', // helm charts api middleware
KUBE_RESOURCE_APPLIER: "/api-resource",
},
// express.js port
LOCAL_SERVER_PORT: Number(process.env.LOCAL_SERVER_PORT || 8889),
WEBPACK_DEV_SERVER_PORT: Number(process.env.LOCAL_SERVER_PORT || 8080),
// session
SESSION_NAME: process.env.SESSION_NAME || "lens-s3ss10n",
SESSION_SECRET: process.env.SESSION_SECRET || "k0nt3n@-s3cr3t-key",
// kubernetes apis
KUBE_CLUSTER_NAME: process.env.KUBE_CLUSTER_NAME,
KUBE_CLUSTER_URL: process.env.KUBE_CLUSTER_URL || KUBERNETES_SERVICE_URL,
KUBE_USERS_URL: process.env.KUBE_USERS_URL || `http://localhost:9999`,
KUBE_TERMINAL_URL: process.env.KUBE_TERMINAL_URL || `http://localhost:9998`,
KUBE_HELM_URL: process.env.KUBE_HELM_URL || `http://localhost:9292`,
KUBE_RESOURCE_APPLIER_URL: process.env.KUBE_RESOURCE_APPLIER_URL || `http://localhost:9393`,
KUBE_METRICS_URL: process.env.KUBE_METRICS_URL || `http://localhost:9090`, // rbac-proxy-url
// flags define visibility of some ui-parts and pages in dashboard
USER_MANAGEMENT_ENABLED: JSON.parse(process.env.USER_MANAGEMENT_ENABLED || "false"),
CHARTS_ENABLED: JSON.parse(process.env.CHARTS_ENABLED || "false"),
// namespaces
LENS_NAMESPACE: process.env.LENS_NAMESPACE || "kontena-lens",
STATS_NAMESPACE: process.env.STATS_NAMESPACE || "kontena-stats",
SERVICE_ACCOUNT_TOKEN: process.env.SERVICE_ACCOUNT_TOKEN
|| null,
KUBERNETES_CA_CERT: process.env.KUBERNETES_CA_CERT,
KUBERNETES_CLIENT_CERT: process.env.KUBERNETES_CLIENT_CERT || "",
KUBERNETES_CLIENT_KEY: process.env.KUBERNETES_CLIENT_KEY || "",
KUBERNETES_TLS_SKIP: JSON.parse(process.env.KUBERNETES_TLS_SKIP || "false"),
KUBERNETES_NAMESPACE: process.env.KUBERNETES_NAMESPACE || "", // default allowed namespace
}
export function isSecure() {
return IS_PRODUCTION ? !config.KUBERNETES_TLS_SKIP : false;
}
export default config;
// Client-side process.env, must be provided by webpack.DefinePlugin
export const clientVars = {
BUILD_VERSION: config.BUILD_VERSION,
IS_PRODUCTION: config.IS_PRODUCTION,
API_PREFIX: config.API_PREFIX,
LOCAL_SERVER_PORT: config.LOCAL_SERVER_PORT,
}
export type IClientVars = typeof clientVars;
-14
View File
@@ -1,14 +0,0 @@
{
"extends": "../client/tsconfig.json",
"compilerOptions": {
"outDir": "../build",
"module": "commonjs",
"moduleResolution": "node",
"target": "esnext",
"sourceMap": false,
"esModuleInterop": true
},
"include": [
"./app.ts"
]
}
-28
View File
@@ -1,28 +0,0 @@
module.exports = {
transform: {
"^.+\\.tsx?$": "ts-jest"
},
moduleFileExtensions: [
"ts",
"tsx",
"js",
"jsx",
"json"
],
testPathIgnorePatterns: [
"/node_modules/"
],
moduleNameMapper: {
"\\.(scss)$": "identity-obj-proxy",
},
moduleDirectories: ["node_modules"],
setupFilesAfterEnv: ["./setup-tests.js"],
globals: {
"ts-jest": {
"tsConfig": "./test/tsconfig.json"
}
},
roots: [
"../client"
],
};
-4
View File
@@ -1,4 +0,0 @@
const Enzyme = require("enzyme");
const Adapter = require("enzyme-adapter-react-16");
Enzyme.configure({ adapter: new Adapter() });
-8
View File
@@ -1,8 +0,0 @@
{
"extends": "../client/tsconfig.json",
"compilerOptions": {
"esModuleInterop": true,
"jsx": "react",
"target": "es6",
}
}
-125
View File
@@ -1,125 +0,0 @@
// Get kubernetes services and port-forward them to pods at localhost
// To be used in development only
import * as yargs from "yargs"
import * as concurrently from "concurrently"
import chalk from "chalk";
import { find } from "lodash"
import { execSync } from "child_process"
import { Pod } from "../client/api/endpoints/pods.api";
import { Service } from "../client/api/endpoints/service.api";
import config from "../server/config";
var { LOCAL_SERVER_PORT, WEBPACK_DEV_SERVER_PORT, KUBE_TERMINAL_URL, KUBE_METRICS_URL } = config;
var terminalPort = +KUBE_TERMINAL_URL.match(/\d+$/)[0];
var metricsPort = +KUBE_METRICS_URL.match(/\d+$/)[0];
// Configure default options
var { namespaces, portOverride, skipServices, verbose } = yargs.options({
namespaces: {
alias: "n",
describe: "Namespaces to search Services & Pods. Example: --namespaces name1 name2 etc",
array: true,
default: [
"kontena-lens",
"kontena-stats",
],
},
verbose: {
describe: "Show extra logs output. Example: --verbose",
boolean: true,
},
skipServices: {
alias: "s",
describe: "Services to skip. Example: --skipServices myService otherName",
array: true,
default: [],
},
portOverride: {
alias: "o",
describe: "Override local ports. Example: --portOverride.serviceName 1000",
default: {
"dashboard": terminalPort, // terminal is running in dashboard pod's container
"rbac-proxy": metricsPort, // replace default "http" port
"prometheus": metricsPort + 1, // keep available metrics service for testing PromQL results
}
},
}).argv;
interface IServiceForward {
namespace: string;
serviceName: string;
podName: string;
port: number;
localPort?: number;
}
function getServices() {
var forwards: IServiceForward[] = [];
// Search Pod by Service.spec.selector for kubectl port-forward commands
namespaces.forEach(namespace => {
var pods = JSON.parse(execSync(`kubectl get pods -n ${namespace} -o json`).toString());
var services = JSON.parse(execSync(`kubectl get services -n ${namespace} -o json`).toString());
services.items.forEach((service: Service) => {
var serviceName = service.metadata.name;
var port = service.spec.ports && service.spec.ports[0].targetPort;
var podSelector = service.spec.selector;
var pod: Pod = find(pods.items, {
metadata: {
labels: podSelector
}
});
var podName = pod ? pod.metadata.name : null;
var localPort = portOverride[serviceName] || port;
var skipByName = skipServices.includes(serviceName);
var skipByPort = ["http", WEBPACK_DEV_SERVER_PORT, LOCAL_SERVER_PORT].includes(localPort);
if (skipByName || skipByPort || !podName) {
var getReason = () => {
if (skipByName) return "service is excluded in configuration"
if (skipByPort) return "local port already in use"
if (!podName) return `pod not found, selector: ${JSON.stringify(podSelector)}`
};
console.info(
chalk.yellow(
`Skip service: ${chalk.bold(`${namespace}/${serviceName}`)} (${getReason()})`,
`Ports (local/remote): ${chalk.bold(`${localPort}/${port}`)}`,
`Pod: ${chalk.bold(podName)}`
),
)
}
else {
forwards.push({
namespace, serviceName, podName,
port, localPort,
});
}
});
});
return forwards;
}
// Run
var services = getServices();
var commands = services.map(({ podName, localPort, port, namespace }: IServiceForward) => {
return `kubectl port-forward -n ${namespace} ${podName} ${localPort}:${port}`
});
services.forEach(({ serviceName, namespace, podName, port, localPort }, index) => {
console.log(
chalk.blueBright.bold(`[${index + 1}] Port-forward`),
`http://${serviceName}.${namespace}.svc.cluster.local -> http://localhost:${localPort}`,
`(Pod: ${chalk.bold(podName)})`,
);
});
if (verbose) {
console.log(
chalk.bold.grey('Commands:'),
chalk.grey(JSON.stringify(commands, null, 2)),
);
}
concurrently(commands, {
restartTries: 1000,
restartDelay: 1000 * 60,
}).catch(Function);
-144
View File
@@ -1,144 +0,0 @@
import * as path from "path";
import * as webpack from "webpack";
import * as HtmlWebpackPlugin from "html-webpack-plugin";
import * as MiniCssExtractPlugin from "mini-css-extract-plugin";
import * as TerserWebpackPlugin from "terser-webpack-plugin";
import { BUILD_DIR, CLIENT_DIR, clientVars, config } from "./server/config"
export default () => {
const { IS_PRODUCTION } = config;
const srcDir = path.resolve(process.cwd(), CLIENT_DIR);
const buildDir = path.resolve(process.cwd(), BUILD_DIR, CLIENT_DIR);
const tsConfigClientFile = path.resolve(srcDir, "tsconfig.json");
const sassCommonVarsFile = "./components/vars.scss"; // needs to be relative for Windows
return {
entry: {
app: path.resolve(srcDir, "components/app.tsx"),
},
output: {
path: buildDir,
publicPath: '/',
filename: '[name].js',
chunkFilename: 'chunks/[name].js',
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.json']
},
mode: IS_PRODUCTION ? "production" : "development",
devtool: IS_PRODUCTION ? "" : "cheap-module-eval-source-map",
optimization: {
minimize: IS_PRODUCTION,
minimizer: [
...(!IS_PRODUCTION ? [] : [
new TerserWebpackPlugin({
cache: true,
parallel: true,
terserOptions: {
mangle: true,
compress: true,
keep_classnames: true,
keep_fnames: true,
},
extractComments: {
condition: "some",
banner: [
`Lens - The Kubernetes IDE. Copyright ${new Date().getFullYear()} by Lakend Labs, Inc. All rights reserved.`
].join("\n")
}
})
]),
],
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
"babel-loader",
{
loader: 'ts-loader',
options: {
configFile: tsConfigClientFile
}
}
]
},
{
test: /\.(jpg|png|svg|map|ico)$/,
use: 'file-loader?name=assets/[name]-[hash:6].[ext]'
},
{
test: /\.(ttf|eot|woff2?)$/,
use: 'file-loader?name=fonts/[name].[ext]'
},
{
test: /\.ya?ml$/,
use: "yml-loader"
},
{
test: /\.s?css$/,
use: [
IS_PRODUCTION ? MiniCssExtractPlugin.loader : {
loader: "style-loader",
options: {}
},
{
loader: "css-loader",
options: {
sourceMap: !IS_PRODUCTION
},
},
{
loader: "sass-loader",
options: {
sourceMap: !IS_PRODUCTION,
prependData: '@import "' + sassCommonVarsFile + '";',
sassOptions: {
includePaths: [srcDir]
},
}
},
]
}
]
},
plugins: [
...(IS_PRODUCTION ? [] : [
new webpack.HotModuleReplacementPlugin(),
]),
new webpack.DefinePlugin({
process: {
env: JSON.stringify(clientVars)
},
}),
// don't include all moment.js locales by default
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new HtmlWebpackPlugin({
template: 'index.html',
inject: true,
hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
}),
],
}
};
-10991
View File
File diff suppressed because it is too large. Load diff
@@ -1,6 +1,6 @@
import { Feature, FeatureStatus } from "../main/feature"
import {KubeConfig, AppsV1Api, RbacAuthorizationV1Api} from "@kubernetes/client-node"
import * as semver from "semver"
import semver from "semver"
import { Cluster } from "../main/cluster";
import * as k8s from "@kubernetes/client-node"
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 754 KiB

File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File renamed without changes.
@@ -1,20 +1,18 @@
import { KubeConfig } from "@kubernetes/client-node"
import { PromiseIpc } from "electron-promise-ipc"
import * as http from "http"
import http from "http"
import { Cluster, ClusterBaseInfo } from "./cluster"
import { clusterStore } from "../common/cluster-store"
import * as k8s from "./k8s"
import logger from "./logger"
import { LensProxy } from "./proxy"
import { app } from "electron"
import * as path from "path"
import path from "path"
import { promises } from "fs"
import { ensureDir } from "fs-extra"
import * as filenamify from "filenamify"
import filenamify from "filenamify"
import { v4 as uuid } from "uuid"
declare const __static: string;
export type FeatureInstallRequest = {
name: string;
clusterId: string;
+1 -1
View File
@@ -7,7 +7,7 @@ import { KubeConfig, CoreV1Api, AuthorizationV1Api, V1ResourceAttributes } from
import * as fm from "./feature-manager";
import { Kubectl } from "./kubectl";
import { PromiseIpc } from "electron-promise-ipc"
import * as request from "request-promise-native"
import request from "request-promise-native"
import { KubeconfigManager } from "./kubeconfig-manager"
enum ClusterStatus {
@@ -1,6 +1,5 @@
import { KubeConfig, CoreV1Api } from "@kubernetes/client-node"
import { readFileSync } from "fs"
import * as http from "http"
import { CoreV1Api, KubeConfig } from "@kubernetes/client-node"
import http from "http"
import { ServerOptions } from "http-proxy"
import * as url from "url"
import logger from "./logger"
@@ -8,8 +7,7 @@ import { getFreePort } from "./port"
import { KubeAuthProxy } from "./kube-auth-proxy"
import { Cluster, ClusterPreferences } from "./cluster"
import { prometheusProviders } from "../common/prometheus-providers"
import { PrometheusService, PrometheusProvider } from "./prometheus/provider-registry"
import { PrometheusLens } from "./prometheus/lens"
import { PrometheusProvider, PrometheusService } from "./prometheus/provider-registry"
export class ContextHandler {
public contextName: string
@@ -76,12 +74,14 @@ export class ContextHandler {
if (clusterPreferences && clusterPreferences.prometheus) {
const prom = clusterPreferences.prometheus
this.prometheusPath = `${prom.namespace}/services/${prom.service}:${prom.port}`
} else {
}
else {
this.prometheusPath = null
}
if(clusterPreferences && clusterPreferences.clusterName) {
if (clusterPreferences && clusterPreferences.clusterName) {
this.clusterName = clusterPreferences.clusterName;
} else {
}
else {
this.clusterName = this.contextName;
}
}
@@ -110,7 +110,8 @@ export class ContextHandler {
const service = resolvedPrometheusServices.filter(n => n)[0]
if (service) {
return service
} else {
}
else {
return {
id: "lens",
namespace: "lens-metrics",
@@ -162,7 +163,7 @@ export class ContextHandler {
let serverPort: number = null
try {
serverPort = await getFreePort()
} catch(error) {
} catch (error) {
logger.error(error)
throw(error)
}
@@ -178,7 +179,7 @@ export class ContextHandler {
public async withTemporaryKubeconfig(callback: (kubeconfig: string) => Promise<any>) {
try {
await callback(this.cluster.kubeconfigPath())
} catch(error) {
} catch (error) {
throw(error)
}
}
@@ -203,7 +204,9 @@ export class ContextHandler {
}
public proxyServerError() {
if (!this.proxyServer) { return null }
if (!this.proxyServer) {
return null
}
return this.proxyServer.lastError
}
File renamed without changes.
+2 -2
View File
@@ -1,5 +1,5 @@
import * as fs from "fs";
import * as path from "path"
import fs from "fs";
import path from "path"
import * as hb from "handlebars"
import { ResourceApplier } from "./resource-applier"
import { KubeConfig, CoreV1Api, Watch } from "@kubernetes/client-node"
@@ -1,4 +1,4 @@
import * as fs from "fs"
import fs from "fs"
export function ensureDir(dirname: string) {
if (!fs.existsSync(dirname)) {
File renamed without changes.
@@ -1,4 +1,4 @@
import * as fs from "fs";
import fs from "fs";
import * as yaml from "js-yaml";
import { HelmRepo, HelmRepoManager } from "./helm-repo-manager"
import logger from "./logger";
+10 -11
View File
@@ -1,5 +1,7 @@
import * as path from "path"
import packageInfo from "../package.json"
import path from "path"
import { LensBinary, LensBinaryOpts } from "./lens-binary"
import { isProduction } from "../common/vars";
export class HelmCli extends LensBinary {
@@ -13,7 +15,7 @@ export class HelmCli extends LensBinary {
super(opts)
}
protected getTarName(): string|null {
protected getTarName(): string | null {
return `${this.binaryName}-v${this.binaryVersion}-${this.platformName}-${this.arch}.tar.gz`
}
@@ -26,19 +28,16 @@ export class HelmCli extends LensBinary {
}
protected getOriginalBinaryPath() {
return path.join(this.dirname, this.platformName+"-"+this.arch, this.originalBinaryName)
return path.join(this.dirname, this.platformName + "-" + this.arch, this.originalBinaryName)
}
}
const helmVersion = require("../../package.json").config.bundledHelmVersion
const isDevelopment = process.env.NODE_ENV !== "production"
let baseDir: string = null
const helmVersion = packageInfo.config.bundledHelmVersion;
let baseDir = process.resourcesPath;
if(isDevelopment) {
baseDir = path.join(process.cwd(), "binaries", "client")
} else {
baseDir = path.join(process.resourcesPath)
if (!isProduction) {
baseDir = path.join(process.cwd(), "binaries", "client");
}
export const helmCli = new HelmCli(baseDir, helmVersion)
export const helmCli = new HelmCli(baseDir, helmVersion);
@@ -1,25 +1,24 @@
import * as tempy from "tempy";
import * as fs from "fs";
import fs from "fs";
import * as yaml from "js-yaml";
import * as camelcaseKeys from "camelcase-keys";
import { promiseExec} from "./promise-exec"
import { helmCli } from "./helm-cli";
import { Cluster } from "./cluster";
import { toCamelCase } from "../common/utils/camelCase";
export class HelmReleaseManager {
public async listReleases(pathToKubeconfig: string, namespace?: string) {
const helm = await helmCli.binaryPath()
const namespaceFlag = namespace ? `-n ${namespace}` : "--all-namespaces"
const { stdout, stderr } = await promiseExec(`"${helm}" ls --output json ${namespaceFlag} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr)})
const { stdout } = await promiseExec(`"${helm}" ls --output json ${namespaceFlag} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr)})
const output = JSON.parse(stdout)
if (output.length == 0) {
return output
}
const result: any = []
output.forEach((release: any, index: number) => {
output[index] = camelcaseKeys(release)
output[index] = toCamelCase(release)
});
return output
}
@@ -1,4 +1,4 @@
import * as fs from "fs";
import fs from "fs";
import logger from "./logger";
import * as yaml from "js-yaml";
import { promiseExec } from "./promise-exec";
File renamed without changes.
+12 -9
View File
@@ -1,7 +1,10 @@
// Main process
import "../common/system-ca"
import "../common/prometheus-providers"
import { app, dialog, protocol } from "electron"
import { PromiseIpc } from "electron-promise-ipc"
import * as path from "path"
import path from "path"
import { format as formatUrl } from "url"
import logger from "./logger"
import initMenu from "./menu"
@@ -15,18 +18,17 @@ import { shellSync } from "./shell-sync"
import { getFreePort } from "./port"
import { mangleProxyEnv } from "./proxy-env"
import { findMainWebContents } from "./webcontents"
import "../common/prometheus-providers"
import { isDevelopment, isMac } from "../common/vars";
mangleProxyEnv()
if (app.commandLine.getSwitchValue("proxy-server") !== "") {
process.env.HTTPS_PROXY = app.commandLine.getSwitchValue("proxy-server")
}
const isDevelopment = process.env.NODE_ENV !== "production"
const promiseIpc = new PromiseIpc({ timeout: 2000 })
let windowManager: WindowManager = null;
let clusterManager: ClusterManager = null;
const vmURL = (isDevelopment) ? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}` : formatUrl({
const vmURL = isDevelopment ? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}` : formatUrl({
pathname: path.join(__dirname, "index.html"),
protocol: "file",
slashes: true,
@@ -41,7 +43,7 @@ async function main() {
tracker.event("app", "start");
protocol.registerFileProtocol('store', (request, callback) => {
const url = request.url.substr(8)
callback( path.normalize(`${app.getPath("userData")}/${url}`) )
callback(path.normalize(`${app.getPath("userData")}/${url}`))
}, (error) => {
if (error) console.error('Failed to register protocol')
})
@@ -80,7 +82,7 @@ async function main() {
},
showPreferencesHook: async () => {
// IPC send needs webContents as we're sending it to renderer
promiseIpc.send('navigate', findMainWebContents(), {name: 'preferences-page'}).then((data: any) => {
promiseIpc.send('navigate', findMainWebContents(), { name: 'preferences-page' }).then((data: any) => {
logger.debug("navigate: preferences IPC sent");
})
},
@@ -103,12 +105,13 @@ async function main() {
}
app.on("ready", main)
app.on('window-all-closed', function() {
app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform != 'darwin') {
if (!isMac) {
app.quit();
} else {
}
else {
windowManager = null
if (clusterManager) clusterManager.stop()
}
-2
View File
@@ -1,8 +1,6 @@
import * as k8s from "@kubernetes/client-node"
import * as os from "os"
import { all } from "q";
import * as yaml from "js-yaml"
import { V1beta1ValidatingWebhookConfiguration } from "@kubernetes/client-node";
import logger from "./logger";
const kc = new k8s.KubeConfig()
File renamed without changes.
@@ -1,5 +1,5 @@
import { app } from "electron"
import * as fs from "fs"
import fs from "fs"
import { ensureDir, randomFileName} from "./file-helpers"
import logger from "./logger"
+57 -37
View File
@@ -1,17 +1,19 @@
import packageInfo from "../package.json"
import { app, remote } from "electron"
import * as path from "path"
import * as fs from "fs"
import * as request from "request"
import * as requestPromise from "request-promise-native"
import path from "path"
import fs from "fs"
import request from "request"
import requestPromise from "request-promise-native"
import logger from "./logger"
import { ensureDir, pathExists } from "fs-extra"
import * as md5File from "md5-file"
import md5File from "md5-file"
import { globalRequestOpts } from "../common/request"
import * as lockFile from "proper-lockfile"
import lockFile from "proper-lockfile"
import { helmCli } from "./helm-cli"
import { userStore } from "../common/user-store"
import { isDevelopment, isMac, isWindows } from "../common/vars";
const bundledVersion = require("../../package.json").config.bundledKubectlVersion
const bundledVersion = packageInfo.config.bundledKubectlVersion;
const kubectlMap: Map<string, string> = new Map([
["1.7", "1.8.15"],
["1.8", "1.9.10"],
@@ -34,16 +36,18 @@ const packageMirrors: Map<string, string> = new Map([
const initScriptVersionString = "# lens-initscript v3\n"
const isDevelopment = process.env.NODE_ENV !== "production"
let bundledPath: string = null
if(isDevelopment) {
if (isDevelopment) {
bundledPath = path.join(process.cwd(), "binaries", "client", process.platform, process.arch, "kubectl")
} else {
}
else {
bundledPath = path.join(process.resourcesPath, process.arch, "kubectl")
}
if(process.platform === "win32") bundledPath = `${bundledPath}.exe`
if (isWindows) {
bundledPath = `${bundledPath}.exe`
}
export class Kubectl {
@@ -60,7 +64,7 @@ export class Kubectl {
// Returns the single bundled Kubectl instance
public static bundled() {
if(!Kubectl.bundledInstance) Kubectl.bundledInstance = new Kubectl(Kubectl.bundledKubectlVersion)
if (!Kubectl.bundledInstance) Kubectl.bundledInstance = new Kubectl(Kubectl.bundledKubectlVersion)
return Kubectl.bundledInstance
}
@@ -69,26 +73,29 @@ export class Kubectl {
const minorVersion = versionParts[1]
/* minorVersion is the first two digits of kube server version
if the version map includes that, use that version, if not, fallback to the exact x.y.z of kube version */
if(kubectlMap.has(minorVersion)) {
if (kubectlMap.has(minorVersion)) {
this.kubectlVersion = kubectlMap.get(minorVersion)
logger.debug("Set kubectl version " + this.kubectlVersion + " for cluster version " + clusterVersion + " using version map")
} else {
}
else {
this.kubectlVersion = versionParts[1] + versionParts[2]
logger.debug("Set kubectl version " + this.kubectlVersion + " for cluster version " + clusterVersion + " using fallback")
}
let arch = null
if(process.arch == "x64") {
if (process.arch == "x64") {
arch = "amd64"
} else if(process.arch == "x86" || process.arch == "ia32") {
}
else if (process.arch == "x86" || process.arch == "ia32") {
arch = "386"
} else {
}
else {
arch = process.arch
}
const platformName = process.platform === "win32" ? "windows" : process.platform
const binaryName = process.platform === "win32" ? "kubectl.exe" : "kubectl"
const platformName = isWindows ? "windows" : process.platform
const binaryName = isWindows ? "kubectl.exe" : "kubectl"
this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${platformName}/${arch}/${binaryName}`
@@ -100,7 +107,7 @@ export class Kubectl {
try {
await this.ensureKubectl()
return this.path
} catch(err) {
} catch (err) {
logger.error("Failed to ensure kubectl, fallback to the bundled version")
logger.error(err)
return Kubectl.bundledKubectlPath
@@ -111,7 +118,7 @@ export class Kubectl {
try {
await this.ensureKubectl()
return this.dirname
} catch(err) {
} catch (err) {
logger.error(err)
return ""
}
@@ -124,7 +131,9 @@ export class Kubectl {
resolveWithFullResponse: true,
timeout: 4000,
...this.getRequestOpts()
}).catch((error) => { logger.error(error) })
}).catch((error) => {
logger.error(error)
})
if (response && response.headers["etag"]) {
return response.headers["etag"].replace(/"/g, "")
@@ -140,7 +149,7 @@ export class Kubectl {
}
const hash = md5File.sync(this.path)
const etag = await this.urlEtag()
if (etag === "") {
if (etag === "") {
logger.debug("Cannot resolve kubectl remote etag")
return true
}
@@ -157,7 +166,7 @@ export class Kubectl {
}
protected async checkBundled(): Promise<boolean> {
if(this.kubectlVersion === Kubectl.bundledKubectlVersion) {
if (this.kubectlVersion === Kubectl.bundledKubectlVersion) {
try {
const exist = await pathExists(this.path)
if (!exist) {
@@ -165,11 +174,12 @@ export class Kubectl {
await fs.promises.chmod(this.path, 0o755)
}
return true
} catch(err) {
} catch (err) {
logger.error("Could not copy the bundled kubectl to app-data: " + err)
return false
}
} else {
}
else {
return false
}
}
@@ -180,10 +190,15 @@ export class Kubectl {
logger.debug(`Acquired a lock for ${this.kubectlVersion}`)
const bundled = await this.checkBundled()
const isValid = await this.checkBinary(!bundled)
if(!isValid) {
await this.downloadKubectl().catch((error) => { logger.error(error) });
if (!isValid) {
await this.downloadKubectl().catch((error) => {
logger.error(error)
});
}
await this.writeInitScripts().catch((error) => { logger.error("Failed to write init scripts"); logger.error(error) })
await this.writeInitScripts().catch((error) => {
logger.error("Failed to write init scripts");
logger.error(error)
})
logger.debug(`Releasing lock for ${this.kubectlVersion}`)
release()
return true
@@ -206,16 +221,19 @@ export class Kubectl {
const file = fs.createWriteStream(this.path)
stream.on("complete", () => {
logger.debug("kubectl binary download finished")
file.end(() => {})
file.end(() => {
})
})
stream.on("error", (error) => {
logger.error(error)
fs.unlink(this.path, () => {})
fs.unlink(this.path, () => {
})
reject(error)
})
file.on("close", () => {
logger.debug("kubectl binary download closed")
fs.chmod(this.path, 0o755, () => {})
fs.chmod(this.path, 0o755, () => {
})
resolve()
})
stream.pipe(file)
@@ -224,7 +242,7 @@ export class Kubectl {
protected async scriptIsLatest(scriptPath: string) {
const scriptExists = await pathExists(scriptPath)
if(!scriptExists) return false
if (!scriptExists) return false
try {
const filehandle = await fs.promises.open(scriptPath, 'r')
@@ -243,7 +261,7 @@ export class Kubectl {
const fsPromises = fs.promises;
const bashScriptPath = path.join(this.dirname, '.bash_set_path')
const bashScriptIsLatest = await this.scriptIsLatest(bashScriptPath)
if(!bashScriptIsLatest) {
if (!bashScriptIsLatest) {
let bashScript = "" + initScriptVersionString
bashScript += "tempkubeconfig=\"$KUBECONFIG\"\n"
bashScript += "test -f \"/etc/profile\" && . \"/etc/profile\"\n"
@@ -262,7 +280,7 @@ export class Kubectl {
const zshScriptPath = path.join(this.dirname, '.zlogin')
const zshScriptIsLatest = await this.scriptIsLatest(zshScriptPath)
if(!zshScriptIsLatest) {
if (!zshScriptIsLatest) {
let zshScript = "" + initScriptVersionString
zshScript += "tempkubeconfig=\"$KUBECONFIG\"\n"
@@ -296,11 +314,13 @@ export class Kubectl {
}
protected getDownloadMirror() {
if (process.platform == "darwin") {
if (isMac) {
return packageMirrors.get("default") // MacOS packages are only available from default
}
const mirror = packageMirrors.get(userStore.getPreferences().downloadMirror)
if (mirror) { return mirror }
if (mirror) {
return mirror
}
return packageMirrors.get("default")
}
+1 -1
View File
@@ -1,4 +1,4 @@
import * as http from "http";
import http from "http";
export abstract class LensApi {
protected respondJson(res: http.ServerResponse, content: {}, status = 200) {
+30 -20
View File
@@ -1,9 +1,10 @@
import * as path from "path"
import * as fs from "fs"
import * as request from "request"
import path from "path"
import fs from "fs"
import request from "request"
import logger from "./logger"
import { ensureDir, pathExists } from "fs-extra"
import * as tar from "tar"
import { isWindows } from "../common/vars";
export type LensBinaryOpts = {
version: string;
@@ -12,6 +13,7 @@ export type LensBinaryOpts = {
newBinaryName?: string;
requestOpts?: request.Options;
}
export class LensBinary {
public binaryVersion: string
@@ -35,19 +37,21 @@ export class LensBinary {
let arch = null
if(process.arch == "x64") {
if (process.arch == "x64") {
arch = "amd64"
} else if(process.arch == "x86" || process.arch == "ia32") {
}
else if (process.arch == "x86" || process.arch == "ia32") {
arch = "386"
} else {
}
else {
arch = process.arch
}
this.arch = arch
this.platformName = process.platform === "win32" ? "windows" : process.platform
this.platformName = isWindows ? "windows" : process.platform
this.dirname = path.normalize(path.join(baseDir, this.binaryName))
if (process.platform === "win32") {
this.binaryName = this.binaryName+".exe"
this.originalBinaryName = this.originalBinaryName+".exe"
if (isWindows) {
this.binaryName = this.binaryName + ".exe"
this.originalBinaryName = this.originalBinaryName + ".exe"
}
const tarName = this.getTarName()
if (tarName) {
@@ -64,7 +68,7 @@ export class LensBinary {
return this.getBinaryPath()
}
protected getTarName(): string|null {
protected getTarName(): string | null {
return null
}
@@ -88,7 +92,7 @@ export class LensBinary {
try {
await this.ensureBinary()
return this.dirname
} catch(err) {
} catch (err) {
logger.error(err)
return ""
}
@@ -101,10 +105,12 @@ export class LensBinary {
public async ensureBinary() {
const isValid = await this.checkBinary()
if(!isValid) {
await this.downloadBinary().catch((error) => { logger.error(error) });
if (!isValid) {
await this.downloadBinary().catch((error) => {
logger.error(error)
});
if (this.tarPath) await this.untarBinary()
if(this.originalBinaryName != this.binaryName ) await this.renameBinary()
if (this.originalBinaryName != this.binaryName) await this.renameBinary()
logger.info(`${this.originalBinaryName} has been downloaded to ${this.getBinaryPath()}`)
}
}
@@ -127,7 +133,8 @@ export class LensBinary {
fs.rename(this.getOriginalBinaryPath(), this.getBinaryPath(), (err) => {
if (err) {
reject(err)
} else {
}
else {
resolve()
}
})
@@ -135,7 +142,7 @@ export class LensBinary {
}
protected async downloadBinary() {
const binaryPath = this.tarPath || this.getBinaryPath()
const binaryPath = this.tarPath || this.getBinaryPath()
await ensureDir(this.getBinaryDir(), 0o755)
const file = fs.createWriteStream(binaryPath)
@@ -152,18 +159,21 @@ export class LensBinary {
stream.on("complete", () => {
logger.info(`Download of ${this.originalBinaryName} finished`)
file.end(() => {})
file.end(() => {
})
})
stream.on("error", (error) => {
logger.error(error)
fs.unlink(binaryPath, () => {})
fs.unlink(binaryPath, () => {
})
throw(error)
})
return new Promise((resolve, reject) => {
file.on("close", () => {
logger.debug(`${this.originalBinaryName} binary download closed`)
if(!this.tarPath) fs.chmod(binaryPath, 0o755, () => {})
if (!this.tarPath) fs.chmod(binaryPath, 0o755, () => {
})
resolve()
})
stream.pipe(file)
File renamed without changes.
+19 -19
View File
@@ -1,4 +1,7 @@
import {app, dialog, Menu, MenuItemConstructorOptions, shell, webContents, BrowserWindow, MenuItem} from "electron"
import { app, BrowserWindow, dialog, Menu, MenuItem, MenuItemConstructorOptions, shell, webContents } from "electron"
import { isDevelopment, isMac, isWindows } from "../common/vars";
// todo: refactor + split menu sections to separated files, e.g. menus/file.menu.ts
export interface MenuOptions {
logoutHook: any;
@@ -10,7 +13,6 @@ export interface MenuOptions {
}
function setClusterSettingsEnabled(enabled: boolean) {
const isMac = process.platform === 'darwin';
const menuIndex = isMac ? 1 : 0
Menu.getApplicationMenu().items[menuIndex].submenu.items[1].enabled = enabled
}
@@ -21,7 +23,7 @@ function showAbout(_menuitem: MenuItem, browserWindow: BrowserWindow) {
]
appDetails.push(`Copyright 2020 Lakend Labs, Inc.`)
let title = "Lens"
if (process.platform === "win32") {
if (isWindows) {
title = ` ${title}`
}
dialog.showMessageBoxSync(browserWindow, {
@@ -40,9 +42,6 @@ function showAbout(_menuitem: MenuItem, browserWindow: BrowserWindow) {
* @param ipc the main promiceIpc handle. Needed to be able to hook IPC sending into logout click handler.
*/
export default function initMenu(opts: MenuOptions, promiseIpc: any) {
const isMac = process.platform === 'darwin';
const isDevelopment = process.env.NODE_ENV === 'development';
const mt: MenuItemConstructorOptions[] = [];
const macAppMenu: MenuItemConstructorOptions = {
label: app.getName(),
@@ -67,26 +66,27 @@ export default function initMenu(opts: MenuOptions, promiseIpc: any) {
{ role: 'quit' }
]
};
if(isMac) {
if (isMac) {
mt.push(macAppMenu);
}
let fileMenu: MenuItemConstructorOptions;
if(isMac) {
if (isMac) {
fileMenu = {
label: 'File',
submenu: [{
label: 'Add Cluster...',
click: opts.addClusterHook,
},
{
label: 'Cluster Settings',
click: opts.clusterSettingsHook,
enabled: false
}
{
label: 'Cluster Settings',
click: opts.clusterSettingsHook,
enabled: false
}
]
}
} else {
}
else {
fileMenu = {
label: 'File',
submenu: [
@@ -134,26 +134,26 @@ export default function initMenu(opts: MenuOptions, promiseIpc: any) {
{
label: 'Back',
accelerator: 'CmdOrCtrl+[',
click () {
click() {
webContents.getFocusedWebContents().executeJavaScript('window.history.back()')
}
},
{
label: 'Forward',
accelerator: 'CmdOrCtrl+]',
click () {
click() {
webContents.getFocusedWebContents().executeJavaScript('window.history.forward()')
}
},
{
label: 'Reload',
accelerator: 'CmdOrCtrl+R',
click () {
click() {
webContents.getFocusedWebContents().reload()
}
},
...(isDevelopment ? [
{ role: 'toggleDevTools'} as MenuItemConstructorOptions,
{ role: 'toggleDevTools' } as MenuItemConstructorOptions,
{
label: 'Open Dashboard Devtools',
click() {
@@ -196,7 +196,7 @@ export default function initMenu(opts: MenuOptions, promiseIpc: any) {
label: "What's new?",
click: opts.showWhatsNewHook,
},
...(process.platform !== "darwin" ? [{
...(!isMac ? [{
label: "About Lens",
click: showAbout
} as MenuItemConstructorOptions] : [])
File renamed without changes.
View File
File renamed without changes.
Loaded 100 of 807 files, more files were not shown because too many files have changed in this diff. Show more