mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
1 parent
e810eab39f
commit
b7974827d2
786 files changed
+14567
-24066
No files matched your search
@@ -1,9 +0,0 @@
|
||||
const userStore = {
|
||||
getPreferences: jest.fn(() => {
|
||||
return {
|
||||
downloadMirror: "default"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export { userStore };
|
||||
@@ -1,14 +0,0 @@
|
||||
import { app, remote } from "electron"
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns app version correctly regardless of dev/prod mode and main/renderer differences
|
||||
*/
|
||||
export function getAppVersion(): string {
|
||||
// app is undefined when running in renderer
|
||||
let version = (app || remote.app).getVersion()
|
||||
if(process.env.NODE_ENV === 'development') {
|
||||
version = require("../../package.json").version
|
||||
}
|
||||
return version;
|
||||
}
|
||||
+18
-15
@@ -1,12 +1,12 @@
|
||||
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"
|
||||
import { getAppVersion } from "./utils/app-version";
|
||||
|
||||
export class ClusterStore {
|
||||
private static instance: ClusterStore;
|
||||
@@ -14,8 +14,10 @@ export class ClusterStore {
|
||||
|
||||
private constructor() {
|
||||
this.store = new ElectronStore({
|
||||
name: "lens-cluster-store",
|
||||
// @ts-ignore
|
||||
// fixme: tests are failed without "projectVersion"
|
||||
projectVersion: getAppVersion(),
|
||||
name: "lens-cluster-store",
|
||||
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
||||
migrations: {
|
||||
"2.0.0-beta.2": version200Beta2.migration,
|
||||
@@ -58,7 +60,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 +78,8 @@ export class ClusterStore {
|
||||
}
|
||||
if (index === -1) {
|
||||
clusters.push(storable)
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
clusters[index] = storable
|
||||
}
|
||||
this.store.set("clusters", clusters)
|
||||
@@ -97,7 +102,7 @@ export class ClusterStore {
|
||||
}
|
||||
|
||||
static getInstance(): ClusterStore {
|
||||
if(!ClusterStore.instance) {
|
||||
if (!ClusterStore.instance) {
|
||||
ClusterStore.instance = new ClusterStore();
|
||||
}
|
||||
return ClusterStore.instance;
|
||||
@@ -108,6 +113,4 @@ export class ClusterStore {
|
||||
}
|
||||
}
|
||||
|
||||
const clusterStore: ClusterStore = ClusterStore.getInstance();
|
||||
|
||||
export { clusterStore };
|
||||
export const clusterStore = ClusterStore.getInstance();
|
||||
@@ -0,0 +1,333 @@
|
||||
import mockFs from "mock-fs"
|
||||
import yaml from "js-yaml"
|
||||
import { ClusterStore } from "./cluster-store";
|
||||
import { Cluster } from "../main/cluster";
|
||||
|
||||
// Console.log needs to be called before fs-mocks, see https://github.com/tschaub/mock-fs/issues/234
|
||||
console.log("");
|
||||
|
||||
describe("for an empty config", () => {
|
||||
beforeEach(() => {
|
||||
ClusterStore.resetInstance()
|
||||
const mockOpts = {
|
||||
'tmp': {
|
||||
'lens-cluster-store.json': JSON.stringify({})
|
||||
}
|
||||
}
|
||||
mockFs(mockOpts)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore()
|
||||
})
|
||||
|
||||
it("allows to store and retrieve a cluster", async () => {
|
||||
const cluster = new Cluster({
|
||||
id: 'foo',
|
||||
kubeConfig: 'kubeconfig string',
|
||||
preferences: {
|
||||
terminalCWD: '/tmp',
|
||||
icon: 'path to icon'
|
||||
}
|
||||
})
|
||||
const clusterStore = ClusterStore.getInstance()
|
||||
clusterStore.storeCluster(cluster);
|
||||
const storedCluster = clusterStore.getCluster(cluster.id);
|
||||
expect(storedCluster.kubeConfig).toBe(cluster.kubeConfig)
|
||||
expect(storedCluster.preferences.icon).toBe(cluster.preferences.icon)
|
||||
expect(storedCluster.preferences.terminalCWD).toBe(cluster.preferences.terminalCWD)
|
||||
expect(storedCluster.id).toBe(cluster.id)
|
||||
})
|
||||
|
||||
it("allows to delete a cluster", async () => {
|
||||
const cluster = new Cluster({
|
||||
id: 'foofoo',
|
||||
kubeConfig: 'kubeconfig string',
|
||||
preferences: {
|
||||
terminalCWD: '/tmp'
|
||||
}
|
||||
})
|
||||
const clusterStore = ClusterStore.getInstance()
|
||||
|
||||
clusterStore.storeCluster(cluster);
|
||||
|
||||
const storedCluster = clusterStore.getCluster(cluster.id);
|
||||
expect(storedCluster.id).toBe(cluster.id)
|
||||
|
||||
clusterStore.removeCluster(cluster.id);
|
||||
|
||||
expect(clusterStore.getCluster(cluster.id)).toBe(null)
|
||||
})
|
||||
})
|
||||
|
||||
describe("for a config with existing clusters", () => {
|
||||
beforeEach(() => {
|
||||
ClusterStore.resetInstance()
|
||||
const mockOpts = {
|
||||
'tmp': {
|
||||
'lens-cluster-store.json': JSON.stringify({
|
||||
__internal__: {
|
||||
migrations: {
|
||||
version: "99.99.99"
|
||||
}
|
||||
},
|
||||
clusters: [
|
||||
{
|
||||
id: 'cluster1',
|
||||
kubeConfig: 'foo',
|
||||
preferences: { terminalCWD: '/foo' }
|
||||
},
|
||||
{
|
||||
id: 'cluster2',
|
||||
kubeConfig: 'foo2',
|
||||
preferences: { terminalCWD: '/foo2' }
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
mockFs(mockOpts)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore()
|
||||
})
|
||||
|
||||
it("allows to retrieve a cluster", async () => {
|
||||
const clusterStore = ClusterStore.getInstance()
|
||||
const storedCluster = clusterStore.getCluster('cluster1')
|
||||
expect(storedCluster.kubeConfig).toBe('foo')
|
||||
expect(storedCluster.preferences.terminalCWD).toBe('/foo')
|
||||
expect(storedCluster.id).toBe('cluster1')
|
||||
|
||||
const storedCluster2 = clusterStore.getCluster('cluster2')
|
||||
expect(storedCluster2.kubeConfig).toBe('foo2')
|
||||
expect(storedCluster2.preferences.terminalCWD).toBe('/foo2')
|
||||
expect(storedCluster2.id).toBe('cluster2')
|
||||
})
|
||||
|
||||
it("allows to delete a cluster", async () => {
|
||||
const clusterStore = ClusterStore.getInstance()
|
||||
|
||||
clusterStore.removeCluster('cluster2')
|
||||
|
||||
// Verify the other cluster still exists:
|
||||
const storedCluster = clusterStore.getCluster('cluster1')
|
||||
expect(storedCluster.id).toBe('cluster1')
|
||||
|
||||
const storedCluster2 = clusterStore.getCluster('cluster2')
|
||||
expect(storedCluster2).toBe(null)
|
||||
})
|
||||
|
||||
it("allows to reload a cluster in-place", async () => {
|
||||
const cluster = new Cluster({
|
||||
id: 'cluster1',
|
||||
kubeConfig: 'kubeconfig string',
|
||||
preferences: {
|
||||
terminalCWD: '/tmp'
|
||||
}
|
||||
})
|
||||
|
||||
const clusterStore = ClusterStore.getInstance()
|
||||
clusterStore.reloadCluster(cluster)
|
||||
|
||||
expect(cluster.kubeConfig).toBe('foo')
|
||||
expect(cluster.preferences.terminalCWD).toBe('/foo')
|
||||
expect(cluster.id).toBe('cluster1')
|
||||
})
|
||||
|
||||
it("allows getting all the clusters", async () => {
|
||||
const clusterStore = ClusterStore.getInstance()
|
||||
const storedClusters = clusterStore.getAllClusters()
|
||||
|
||||
expect(storedClusters[0].id).toBe('cluster1')
|
||||
expect(storedClusters[0].preferences.terminalCWD).toBe('/foo')
|
||||
expect(storedClusters[0].kubeConfig).toBe('foo')
|
||||
|
||||
expect(storedClusters[1].id).toBe('cluster2')
|
||||
expect(storedClusters[1].preferences.terminalCWD).toBe('/foo2')
|
||||
expect(storedClusters[1].kubeConfig).toBe('foo2')
|
||||
})
|
||||
|
||||
it("allows storing the clusters in a different order", async () => {
|
||||
const clusterStore = ClusterStore.getInstance()
|
||||
const storedClusters = clusterStore.getAllClusters()
|
||||
|
||||
const reorderedClusters = [storedClusters[1], storedClusters[0]]
|
||||
clusterStore.storeClusters(reorderedClusters)
|
||||
const storedClusters2 = clusterStore.getAllClusters()
|
||||
|
||||
expect(storedClusters2[0].id).toBe('cluster2')
|
||||
expect(storedClusters2[1].id).toBe('cluster1')
|
||||
})
|
||||
})
|
||||
|
||||
describe("for a pre 2.0 config with an existing cluster", () => {
|
||||
beforeEach(() => {
|
||||
ClusterStore.resetInstance()
|
||||
const mockOpts = {
|
||||
'tmp': {
|
||||
'lens-cluster-store.json': JSON.stringify({
|
||||
__internal__: {
|
||||
migrations: {
|
||||
version: "1.0.0"
|
||||
}
|
||||
},
|
||||
cluster1: 'kubeconfig content'
|
||||
})
|
||||
}
|
||||
}
|
||||
mockFs(mockOpts)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore()
|
||||
})
|
||||
|
||||
it("migrates to modern format with kubeconfig under a key", async () => {
|
||||
const clusterStore = ClusterStore.getInstance()
|
||||
const storedCluster = clusterStore.store.get('clusters')[0]
|
||||
expect(storedCluster.kubeConfig).toBe('kubeconfig content')
|
||||
})
|
||||
})
|
||||
|
||||
describe("for a pre 2.4.1 config with an existing cluster", () => {
|
||||
beforeEach(() => {
|
||||
ClusterStore.resetInstance()
|
||||
const mockOpts = {
|
||||
'tmp': {
|
||||
'lens-cluster-store.json': JSON.stringify({
|
||||
__internal__: {
|
||||
migrations: {
|
||||
version: "2.0.0-beta.2"
|
||||
}
|
||||
},
|
||||
cluster1: {
|
||||
kubeConfig: 'foo',
|
||||
online: true,
|
||||
accessible: false,
|
||||
failureReason: 'user error'
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
mockFs(mockOpts)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore()
|
||||
})
|
||||
|
||||
it("migrates to modern format throwing out the state related data", async () => {
|
||||
const clusterStore = ClusterStore.getInstance()
|
||||
const storedClusterData = clusterStore.store.get('clusters')[0]
|
||||
expect(storedClusterData.hasOwnProperty('online')).toBe(false)
|
||||
expect(storedClusterData.hasOwnProperty('accessible')).toBe(false)
|
||||
expect(storedClusterData.hasOwnProperty('failureReason')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("for a pre 2.6.0 config with a cluster that has arrays in auth config", () => {
|
||||
beforeEach(() => {
|
||||
ClusterStore.resetInstance()
|
||||
const mockOpts = {
|
||||
'tmp': {
|
||||
'lens-cluster-store.json': JSON.stringify({
|
||||
__internal__: {
|
||||
migrations: {
|
||||
version: "2.4.1"
|
||||
}
|
||||
},
|
||||
cluster1: {
|
||||
kubeConfig: "apiVersion: v1\nclusters:\n- cluster:\n server: https://10.211.55.6:8443\n name: minikube\ncontexts:\n- context:\n cluster: minikube\n user: minikube\n name: minikube\ncurrent-context: minikube\nkind: Config\npreferences: {}\nusers:\n- name: minikube\n user:\n client-certificate: /Users/kimmo/.minikube/client.crt\n client-key: /Users/kimmo/.minikube/client.key\n auth-provider:\n config:\n access-token:\n - should be string\n expiry:\n - should be string\n"
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
mockFs(mockOpts)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore()
|
||||
})
|
||||
|
||||
it("replaces array format access token and expiry into string", async () => {
|
||||
const clusterStore = ClusterStore.getInstance()
|
||||
const storedClusterData = clusterStore.store.get('clusters')[0]
|
||||
const kc = yaml.safeLoad(storedClusterData.kubeConfig)
|
||||
expect(kc.users[0].user['auth-provider'].config['access-token']).toBe("should be string")
|
||||
expect(kc.users[0].user['auth-provider'].config['expiry']).toBe("should be string")
|
||||
})
|
||||
})
|
||||
|
||||
describe("for a pre 2.6.0 config with a cluster icon", () => {
|
||||
beforeEach(() => {
|
||||
ClusterStore.resetInstance()
|
||||
const mockOpts = {
|
||||
'tmp': {
|
||||
'lens-cluster-store.json': JSON.stringify({
|
||||
__internal__: {
|
||||
migrations: {
|
||||
version: "2.4.1"
|
||||
}
|
||||
},
|
||||
cluster1: {
|
||||
kubeConfig: "foo",
|
||||
icon: "icon path",
|
||||
preferences: {
|
||||
terminalCWD: "/tmp"
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
mockFs(mockOpts)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore()
|
||||
})
|
||||
|
||||
it("moves the icon into preferences", async () => {
|
||||
const clusterStore = ClusterStore.getInstance()
|
||||
const storedClusterData = clusterStore.store.get('clusters')[0]
|
||||
expect(storedClusterData.hasOwnProperty('icon')).toBe(false)
|
||||
expect(storedClusterData.preferences.hasOwnProperty('icon')).toBe(true)
|
||||
expect(storedClusterData.preferences.icon).toBe("icon path")
|
||||
})
|
||||
})
|
||||
|
||||
describe("for a pre 2.7.0-beta.0 config without a workspace", () => {
|
||||
beforeEach(() => {
|
||||
ClusterStore.resetInstance()
|
||||
const mockOpts = {
|
||||
'tmp': {
|
||||
'lens-cluster-store.json': JSON.stringify({
|
||||
__internal__: {
|
||||
migrations: {
|
||||
version: "2.6.6"
|
||||
}
|
||||
},
|
||||
cluster1: {
|
||||
kubeConfig: "foo",
|
||||
icon: "icon path",
|
||||
preferences: {
|
||||
terminalCWD: "/tmp"
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
mockFs(mockOpts)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore()
|
||||
})
|
||||
|
||||
it("adds cluster to default workspace", async () => {
|
||||
const clusterStore = ClusterStore.getInstance()
|
||||
const storedClusterData = clusterStore.store.get("clusters")[0]
|
||||
expect(storedClusterData.workspace).toBe('default')
|
||||
})
|
||||
})
|
||||
@@ -1,13 +0,0 @@
|
||||
/* Early store format had the kubeconfig directly under context name, this moves
|
||||
it under the kubeConfig key */
|
||||
|
||||
export function migration(store: any) {
|
||||
console.log("CLUSTER STORE, MIGRATION: 2.0.0-beta.2");
|
||||
for (const value of store) {
|
||||
const contextName = value[0];
|
||||
// Looping all the keys gives out the store internal stuff too...
|
||||
if(contextName === "__internal__" || value[1].hasOwnProperty('kubeConfig')) continue;
|
||||
|
||||
store.set(contextName, { kubeConfig: value[1] });
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Cleans up a store that had the state related data stored
|
||||
export function migration(store: any) {
|
||||
console.log("CLUSTER STORE, MIGRATION: 2.4.1");
|
||||
for (const value of store) {
|
||||
const contextName = value[0];
|
||||
if(contextName === "__internal__") continue;
|
||||
const cluster = value[1];
|
||||
|
||||
store.set(contextName, { kubeConfig: cluster.kubeConfig, icon: cluster.icon || null, preferences: cluster.preferences || {} });
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// Move cluster icon from root to preferences
|
||||
export function migration(store: any) {
|
||||
console.log("CLUSTER STORE, MIGRATION: 2.6.0-beta.2");
|
||||
for (const value of store) {
|
||||
const clusterKey = value[0];
|
||||
if(clusterKey === "__internal__") continue
|
||||
const cluster = value[1];
|
||||
if(!cluster.preferences) cluster.preferences = {};
|
||||
if(cluster.icon) {
|
||||
cluster.preferences.icon = cluster.icon;
|
||||
delete(cluster["icon"]);
|
||||
}
|
||||
store.set(clusterKey, { contextName: clusterKey, kubeConfig: value[1].kubeConfig, preferences: value[1].preferences });
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import * as yaml from "js-yaml"
|
||||
|
||||
// Convert access token and expiry from arrays into strings
|
||||
export function migration(store: any) {
|
||||
console.log("CLUSTER STORE, MIGRATION: 2.6.0-beta.3");
|
||||
for (const value of store) {
|
||||
const clusterKey = value[0];
|
||||
if(clusterKey === "__internal__") continue
|
||||
const cluster = value[1];
|
||||
if(!cluster.kubeConfig) continue
|
||||
const kubeConfig = yaml.safeLoad(cluster.kubeConfig)
|
||||
if(!kubeConfig.hasOwnProperty('users')) continue
|
||||
const userObj = kubeConfig.users[0]
|
||||
if (userObj) {
|
||||
const user = userObj.user
|
||||
if (user["auth-provider"] && user["auth-provider"].config) {
|
||||
const authConfig = user["auth-provider"].config
|
||||
if (authConfig["access-token"]) {
|
||||
authConfig["access-token"] = `${authConfig["access-token"]}`
|
||||
}
|
||||
if (authConfig.expiry) {
|
||||
authConfig.expiry = `${authConfig.expiry}`
|
||||
}
|
||||
console.log(authConfig)
|
||||
user["auth-provider"].config = authConfig
|
||||
kubeConfig.users = [{
|
||||
name: userObj.name,
|
||||
user: user
|
||||
}]
|
||||
cluster.kubeConfig = yaml.safeDump(kubeConfig)
|
||||
store.set(clusterKey, cluster)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Add existing clusters to "default" workspace
|
||||
export function migration(store: any) {
|
||||
console.log("CLUSTER STORE, MIGRATION: 2.7.0-beta.0");
|
||||
for (const value of store) {
|
||||
const clusterKey = value[0];
|
||||
if(clusterKey === "__internal__") continue
|
||||
const cluster = value[1];
|
||||
cluster.workspace = "default"
|
||||
store.set(clusterKey, cluster)
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// add id for clusters and store them to array
|
||||
import { v4 as uuid } from "uuid"
|
||||
|
||||
export function migration(store: any) {
|
||||
console.log("CLUSTER STORE, MIGRATION: 2.7.0-beta.1");
|
||||
const clusters: any[] = []
|
||||
for (const value of store) {
|
||||
const clusterKey = value[0];
|
||||
if(clusterKey === "__internal__") continue
|
||||
if(clusterKey === "clusters") continue
|
||||
const cluster = value[1];
|
||||
cluster.id = uuid()
|
||||
if (!cluster.preferences.clusterName) {
|
||||
cluster.preferences.clusterName = clusterKey
|
||||
}
|
||||
clusters.push(cluster)
|
||||
store.delete(clusterKey)
|
||||
}
|
||||
if (clusters.length > 0) {
|
||||
store.set("clusters", clusters)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
// Add / reset "lastSeenAppVersion"
|
||||
export function migration(store: any) {
|
||||
store.set("lastSeenAppVersion", "0.0.0");
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Setup static folder for common assets
|
||||
|
||||
import path from "path";
|
||||
import { protocol } from "electron"
|
||||
import logger from "../main/logger";
|
||||
import { staticDir, staticProto, outDir } from "./vars";
|
||||
|
||||
export function registerStaticProtocol(rootFolder = staticDir) {
|
||||
const scheme = staticProto.replace("://", "");
|
||||
protocol.registerFileProtocol(scheme, (request, callback) => {
|
||||
const relativePath = request.url.replace(staticProto, "");
|
||||
const absPath = path.resolve(rootFolder, relativePath);
|
||||
callback(absPath);
|
||||
}, (error) => {
|
||||
logger.debug(`Failed to register protocol "${scheme}"`, error);
|
||||
})
|
||||
}
|
||||
|
||||
export function getStaticUrl(filePath: string) {
|
||||
return staticProto + filePath;
|
||||
}
|
||||
|
||||
export function getStaticPath(filePath: string) {
|
||||
return path.resolve(staticDir, filePath);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as request from "request"
|
||||
import { userStore } from "../common/user-store"
|
||||
import request from "request"
|
||||
import { userStore } from "./user-store"
|
||||
|
||||
export function globalRequestOpts(requestOpts: request.Options ) {
|
||||
const userPrefs = userStore.getPreferences()
|
||||
|
||||
+11
-3
@@ -1,6 +1,14 @@
|
||||
import * as winca from "win-ca/api"
|
||||
import "mac-ca"
|
||||
import { isMac, isWindows } from "./vars";
|
||||
import winca from "win-ca"
|
||||
import macca from "mac-ca"
|
||||
import logger from "../main/logger"
|
||||
|
||||
if (process.platform === "win32") {
|
||||
if (isMac) {
|
||||
for (const crt of macca.all()) {
|
||||
const attributes = crt.issuer?.attributes?.map((a: any) => `${a.name}=${a.value}`)
|
||||
logger.debug("Using host CA: " + attributes.join(","))
|
||||
}
|
||||
}
|
||||
if (isWindows) {
|
||||
winca.inject("+") // see: https://github.com/ukoloff/win-ca#caveats
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { machineIdSync } from 'node-machine-id'
|
||||
import { userStore } from "../common/user-store"
|
||||
import * as ua from "universal-analytics"
|
||||
import ua from "universal-analytics"
|
||||
import { machineIdSync } from "node-machine-id"
|
||||
import { userStore } from "./user-store"
|
||||
|
||||
const GA_ID = "UA-159377374-1"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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"
|
||||
import { getAppVersion } from "./utils/app-version";
|
||||
|
||||
export interface User {
|
||||
id?: string;
|
||||
@@ -20,7 +20,9 @@ export class UserStore {
|
||||
|
||||
private constructor() {
|
||||
this.store = new ElectronStore({
|
||||
projectVersion: appUtil.getAppVersion(),
|
||||
// @ts-ignore
|
||||
// fixme: tests are failed without "projectVersion"
|
||||
projectVersion: getAppVersion(),
|
||||
migrations: {
|
||||
"2.1.0-beta.4": version210Beta4.migration,
|
||||
}
|
||||
@@ -68,7 +70,7 @@ export class UserStore {
|
||||
}
|
||||
|
||||
static getInstance(): UserStore {
|
||||
if(!UserStore.instance) {
|
||||
if (!UserStore.instance) {
|
||||
UserStore.instance = new UserStore();
|
||||
}
|
||||
return UserStore.instance;
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import mockFs from "mock-fs"
|
||||
import { userStore, UserStore } from "./user-store"
|
||||
|
||||
// Console.log needs to be called before fs-mocks, see https://github.com/tschaub/mock-fs/issues/234
|
||||
console.log("");
|
||||
|
||||
describe("for an empty config", () => {
|
||||
beforeEach(() => {
|
||||
UserStore.resetInstance()
|
||||
const mockOpts = {
|
||||
'tmp': {
|
||||
'config.json': JSON.stringify({})
|
||||
}
|
||||
}
|
||||
mockFs(mockOpts)
|
||||
const userStore = UserStore.getInstance()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore()
|
||||
})
|
||||
|
||||
it("allows setting and retrieving lastSeenAppVersion", async () => {
|
||||
userStore.setLastSeenAppVersion("1.2.3");
|
||||
expect(userStore.lastSeenAppVersion()).toBe("1.2.3");
|
||||
})
|
||||
|
||||
it("allows adding and listing seen contexts", async () => {
|
||||
userStore.storeSeenContext(['foo'])
|
||||
expect(userStore.getSeenContexts().length).toBe(1)
|
||||
userStore.storeSeenContext(['foo', 'bar'])
|
||||
const seenContexts = userStore.getSeenContexts()
|
||||
expect(seenContexts.length).toBe(2) // check 'foo' isn't added twice
|
||||
expect(seenContexts[0]).toBe('foo')
|
||||
expect(seenContexts[1]).toBe('bar')
|
||||
})
|
||||
|
||||
it("allows setting and getting preferences", async () => {
|
||||
userStore.setPreferences({
|
||||
httpsProxy: 'abcd://defg',
|
||||
})
|
||||
const storedPreferences = userStore.getPreferences()
|
||||
expect(storedPreferences.httpsProxy).toBe('abcd://defg')
|
||||
expect(storedPreferences.colorTheme).toBe('dark') // defaults to dark
|
||||
userStore.setPreferences({
|
||||
colorTheme: 'light'
|
||||
})
|
||||
expect(userStore.getPreferences().colorTheme).toBe('light')
|
||||
})
|
||||
})
|
||||
|
||||
describe("migrations", () => {
|
||||
beforeEach(() => {
|
||||
UserStore.resetInstance()
|
||||
const mockOpts = {
|
||||
'tmp': {
|
||||
'config.json': JSON.stringify({
|
||||
user: { username: 'foobar' },
|
||||
preferences: { colorTheme: 'light' },
|
||||
lastSeenAppVersion: '1.2.3'
|
||||
})
|
||||
}
|
||||
}
|
||||
mockFs(mockOpts)
|
||||
const userStore = UserStore.getInstance()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore()
|
||||
})
|
||||
|
||||
it("sets last seen app version to 0.0.0", async () => {
|
||||
expect(userStore.lastSeenAppVersion()).toBe('0.0.0')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,9 @@
|
||||
import packageInfo from "../../../package.json"
|
||||
|
||||
export function getAppVersion(): string {
|
||||
return packageInfo.version;
|
||||
}
|
||||
|
||||
export function getBundledKubectlVersion(): string {
|
||||
return packageInfo.config.bundledKubectlVersion;
|
||||
}
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
// Encode/decode utf-8 base64 string
|
||||
import * as Base64 from "crypto-js/enc-base64";
|
||||
import * as Utf8 from "crypto-js/enc-utf8";
|
||||
|
||||
export const base64 = {
|
||||
decode(data: string) {
|
||||
return Base64.parse(data).toString(Utf8);
|
||||
},
|
||||
encode(data: string) {
|
||||
return Utf8.parse(data).toString(Base64);
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// Common utils (main/renderer)
|
||||
|
||||
export * from "./base64"
|
||||
export * from "./camelCase"
|
||||
export * from "./splitArray"
|
||||
@@ -0,0 +1,19 @@
|
||||
// Moved from dashboard/client/utils/arrays.ts
|
||||
/**
|
||||
* This function splits an array into two sub arrays on the first instance of
|
||||
* element (from the left). If the array does not contain the element. The
|
||||
* return value is defined to be `[array, [], false]`. If the element is in
|
||||
* the array then the return value is `[left, right, true]` where `left` is
|
||||
* the elements of `array` from `[0, index)` and `right` is `(index, length)`
|
||||
* @param array the full array to split into two sub-arrays
|
||||
* @param element the element in the middle of the array
|
||||
* @returns the left and right sub-arrays which when conjoined with `element`
|
||||
* is the same as `array`, and `true`
|
||||
*/
|
||||
export function splitArray<T>(array: T[], element: T): [T[], T[], boolean] {
|
||||
const index = array.indexOf(element);
|
||||
if (index < 0) {
|
||||
return [array, [], false];
|
||||
}
|
||||
return [array.slice(0, index), array.slice(index + 1, array.length), true]
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { splitArray } from "./splitArray";
|
||||
|
||||
describe("split array on element tests", () => {
|
||||
test("empty array", () => {
|
||||
expect(splitArray([], 10)).toStrictEqual([[], [], false]);
|
||||
});
|
||||
|
||||
test("one element, not in array", () => {
|
||||
expect(splitArray([1], 10)).toStrictEqual([[1], [], false]);
|
||||
});
|
||||
|
||||
test("ten elements, not in array", () => {
|
||||
expect(splitArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 10)).toStrictEqual([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [], false]);
|
||||
});
|
||||
|
||||
test("one elements, in array", () => {
|
||||
expect(splitArray([1], 1)).toStrictEqual([[], [], true]);
|
||||
});
|
||||
|
||||
test("ten elements, in front array", () => {
|
||||
expect(splitArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0)).toStrictEqual([[], [1, 2, 3, 4, 5, 6, 7, 8, 9], true]);
|
||||
});
|
||||
|
||||
test("ten elements, in middle array", () => {
|
||||
expect(splitArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 4)).toStrictEqual([[0, 1, 2, 3], [5, 6, 7, 8, 9], true]);
|
||||
});
|
||||
|
||||
test("ten elements, in end array", () => {
|
||||
expect(splitArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 9)).toStrictEqual([[0, 1, 2, 3, 4, 5, 6, 7, 8], [], true]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
// App's common configuration for any process (main, renderer, build pipeline, etc.)
|
||||
import path from "path";
|
||||
|
||||
// Temp
|
||||
export const reactAppName = "app_react"
|
||||
export const vueAppName = "app_vue"
|
||||
|
||||
// Flags
|
||||
export const isMac = process.platform === "darwin"
|
||||
export const isWindows = process.platform === "win32"
|
||||
export const isDebugging = process.env.DEBUG === "true";
|
||||
export const isProduction = process.env.NODE_ENV === "production"
|
||||
export const isDevelopment = isDebugging || !isProduction;
|
||||
export const buildVersion = process.env.BUILD_VERSION;
|
||||
export const isTestEnv = !!process.env.JEST_WORKER_ID;
|
||||
|
||||
// Paths
|
||||
export const contextDir = process.cwd();
|
||||
export const staticDir = path.join(contextDir, "static");
|
||||
export const outDir = path.join(contextDir, "out");
|
||||
export const mainDir = path.join(contextDir, "src/main");
|
||||
export const rendererDir = path.join(contextDir, "src/renderer");
|
||||
export const htmlTemplate = path.resolve(rendererDir, "template.html");
|
||||
export const sassCommonVars = path.resolve(rendererDir, "components/vars.scss");
|
||||
|
||||
// Apis
|
||||
export const staticProto = "static://"
|
||||
|
||||
export const apiPrefix = {
|
||||
BASE: '/api',
|
||||
TERMINAL: '/api-terminal', // terminal api
|
||||
KUBE_BASE: '/api-kube', // kubernetes cluster api
|
||||
KUBE_HELM: '/api-helm', // helm charts api
|
||||
KUBE_RESOURCE_APPLIER: "/api-resource",
|
||||
};
|
||||
|
||||
// Links
|
||||
export const issuesTrackerUrl = "https://github.com/lensapp/lens/issues"
|
||||
export const slackUrl = "https://join.slack.com/t/k8slens/shared_invite/enQtOTc5NjAyNjYyOTk4LWU1NDQ0ZGFkOWJkNTRhYTc2YjVmZDdkM2FkNGM5MjhiYTRhMDU2NDQ1MzIyMDA4ZGZlNmExOTc0N2JmY2M3ZGI"
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as ElectronStore from "electron-store"
|
||||
import ElectronStore from "electron-store"
|
||||
import { clusterStore } from "./cluster-store"
|
||||
|
||||
export interface WorkspaceData {
|
||||
|
||||
Reference in new issue
Block a user