mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Ignore clusters with invalid kubeconfig (#1956)
* Ignore clusters with invalid kubeconfig Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Improve error message Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Mark cluster as dead if kubeconfig loading fails Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Fix tests Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Validate cluster object in kubeconfig when constructing cluster Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Add unit tests for validateKubeConfig Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Refactor validateKubeconfig unit tests Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Extract ValidationOpts type Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Add default value to validationOpts param Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Change isDead to property Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Fix lint issues Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Add missing new line Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Update validateKubeConfig in-code documentation Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Remove isDead property Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Display warning notification if invalid kubeconfig detected (#2233) * Display warning notification if invalid kubeconfig detected Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com>
This commit is contained in:
1 parent
e837e6f1db
commit
4f74b9aabe
12 files changed
+307
-30
No files matched your search
@@ -6,6 +6,29 @@ import { ClusterStore, getClusterIdFromHost } from "../cluster-store";
|
||||
import { workspaceStore } from "../workspace-store";
|
||||
|
||||
const testDataIcon = fs.readFileSync("test-data/cluster-store-migration-icon.png");
|
||||
const kubeconfig = `
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: https://localhost
|
||||
name: test
|
||||
contexts:
|
||||
- context:
|
||||
cluster: test
|
||||
user: test
|
||||
name: foo
|
||||
- context:
|
||||
cluster: test
|
||||
user: test
|
||||
name: foo2
|
||||
current-context: test
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: test
|
||||
user:
|
||||
token: kubeconfig-user-q4lm4:xxxyyyy
|
||||
`;
|
||||
|
||||
jest.mock("electron", () => {
|
||||
return {
|
||||
@@ -47,13 +70,13 @@ describe("empty config", () => {
|
||||
clusterStore.addCluster(
|
||||
new Cluster({
|
||||
id: "foo",
|
||||
contextName: "minikube",
|
||||
contextName: "foo",
|
||||
preferences: {
|
||||
terminalCWD: "/tmp",
|
||||
icon: "data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5",
|
||||
clusterName: "minikube"
|
||||
},
|
||||
kubeConfigPath: ClusterStore.embedCustomKubeConfig("foo", "fancy foo config"),
|
||||
kubeConfigPath: ClusterStore.embedCustomKubeConfig("foo", kubeconfig),
|
||||
workspace: workspaceStore.currentWorkspaceId
|
||||
})
|
||||
);
|
||||
@@ -91,20 +114,20 @@ describe("empty config", () => {
|
||||
clusterStore.addClusters(
|
||||
new Cluster({
|
||||
id: "prod",
|
||||
contextName: "prod",
|
||||
contextName: "foo",
|
||||
preferences: {
|
||||
clusterName: "prod"
|
||||
},
|
||||
kubeConfigPath: ClusterStore.embedCustomKubeConfig("prod", "fancy config"),
|
||||
kubeConfigPath: ClusterStore.embedCustomKubeConfig("prod", kubeconfig),
|
||||
workspace: "workstation"
|
||||
}),
|
||||
new Cluster({
|
||||
id: "dev",
|
||||
contextName: "dev",
|
||||
contextName: "foo2",
|
||||
preferences: {
|
||||
clusterName: "dev"
|
||||
},
|
||||
kubeConfigPath: ClusterStore.embedCustomKubeConfig("dev", "fancy config"),
|
||||
kubeConfigPath: ClusterStore.embedCustomKubeConfig("dev", kubeconfig),
|
||||
workspace: "workstation"
|
||||
})
|
||||
);
|
||||
@@ -177,20 +200,20 @@ describe("config with existing clusters", () => {
|
||||
clusters: [
|
||||
{
|
||||
id: "cluster1",
|
||||
kubeConfig: "foo",
|
||||
kubeConfigPath: kubeconfig,
|
||||
contextName: "foo",
|
||||
preferences: { terminalCWD: "/foo" },
|
||||
workspace: "default"
|
||||
},
|
||||
{
|
||||
id: "cluster2",
|
||||
kubeConfig: "foo2",
|
||||
kubeConfigPath: kubeconfig,
|
||||
contextName: "foo2",
|
||||
preferences: { terminalCWD: "/foo2" }
|
||||
},
|
||||
{
|
||||
id: "cluster3",
|
||||
kubeConfig: "foo",
|
||||
kubeConfigPath: kubeconfig,
|
||||
contextName: "foo",
|
||||
preferences: { terminalCWD: "/foo" },
|
||||
workspace: "foo",
|
||||
@@ -247,6 +270,78 @@ describe("config with existing clusters", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("config with invalid cluster kubeconfig", () => {
|
||||
beforeEach(() => {
|
||||
const invalidKubeconfig = `
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: https://localhost
|
||||
name: test2
|
||||
contexts:
|
||||
- context:
|
||||
cluster: test
|
||||
user: test
|
||||
name: test
|
||||
current-context: test
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: test
|
||||
user:
|
||||
token: kubeconfig-user-q4lm4:xxxyyyy
|
||||
`;
|
||||
|
||||
ClusterStore.resetInstance();
|
||||
const mockOpts = {
|
||||
"tmp": {
|
||||
"lens-cluster-store.json": JSON.stringify({
|
||||
__internal__: {
|
||||
migrations: {
|
||||
version: "99.99.99"
|
||||
}
|
||||
},
|
||||
clusters: [
|
||||
{
|
||||
id: "cluster1",
|
||||
kubeConfigPath: invalidKubeconfig,
|
||||
contextName: "test",
|
||||
preferences: { terminalCWD: "/foo" },
|
||||
workspace: "foo",
|
||||
},
|
||||
{
|
||||
id: "cluster2",
|
||||
kubeConfigPath: kubeconfig,
|
||||
contextName: "foo",
|
||||
preferences: { terminalCWD: "/foo" },
|
||||
workspace: "default"
|
||||
},
|
||||
|
||||
]
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
return clusterStore.load();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("does not enable clusters with invalid kubeconfig", () => {
|
||||
const storedClusters = clusterStore.clustersList;
|
||||
|
||||
expect(storedClusters.length).toBe(2);
|
||||
expect(storedClusters[0].enabled).toBeFalsy;
|
||||
expect(storedClusters[1].id).toBe("cluster2");
|
||||
expect(storedClusters[1].enabled).toBeTruthy;
|
||||
});
|
||||
});
|
||||
|
||||
describe("pre 2.0 config with an existing cluster", () => {
|
||||
beforeEach(() => {
|
||||
ClusterStore.resetInstance();
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import { KubeConfig } from "@kubernetes/client-node";
|
||||
import { validateKubeConfig } from "../kube-helpers";
|
||||
|
||||
const kubeconfig = `
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: https://localhost
|
||||
name: test
|
||||
contexts:
|
||||
- context:
|
||||
cluster: test
|
||||
user: test
|
||||
name: valid
|
||||
- context:
|
||||
cluster: test2
|
||||
user: test
|
||||
name: invalidCluster
|
||||
- context:
|
||||
cluster: test
|
||||
user: test2
|
||||
name: invalidUser
|
||||
- context:
|
||||
cluster: test
|
||||
user: invalidExec
|
||||
name: invalidExec
|
||||
current-context: test
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: test
|
||||
user:
|
||||
exec:
|
||||
command: echo
|
||||
- name: invalidExec
|
||||
user:
|
||||
exec:
|
||||
command: foo
|
||||
`;
|
||||
|
||||
const kc = new KubeConfig();
|
||||
|
||||
describe("validateKubeconfig", () => {
|
||||
beforeAll(() => {
|
||||
kc.loadFromString(kubeconfig);
|
||||
});
|
||||
describe("with default validation options", () => {
|
||||
describe("with valid kubeconfig", () => {
|
||||
it("does not raise exceptions", () => {
|
||||
expect(() => { validateKubeConfig(kc, "valid");}).not.toThrow();
|
||||
});
|
||||
});
|
||||
describe("with invalid context object", () => {
|
||||
it("it raises exception", () => {
|
||||
expect(() => { validateKubeConfig(kc, "invalid");}).toThrow("No valid context object provided in kubeconfig for context 'invalid'");
|
||||
});
|
||||
});
|
||||
|
||||
describe("with invalid cluster object", () => {
|
||||
it("it raises exception", () => {
|
||||
expect(() => { validateKubeConfig(kc, "invalidCluster");}).toThrow("No valid cluster object provided in kubeconfig for context 'invalidCluster'");
|
||||
});
|
||||
});
|
||||
|
||||
describe("with invalid user object", () => {
|
||||
it("it raises exception", () => {
|
||||
expect(() => { validateKubeConfig(kc, "invalidUser");}).toThrow("No valid user object provided in kubeconfig for context 'invalidUser'");
|
||||
});
|
||||
});
|
||||
|
||||
describe("with invalid exec command", () => {
|
||||
it("it raises exception", () => {
|
||||
expect(() => { validateKubeConfig(kc, "invalidExec");}).toThrow("User Exec command \"foo\" not found on host. Please ensure binary is found in PATH or use absolute path to binary in Kubeconfig");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("with validateCluster as false", () => {
|
||||
describe("with invalid cluster object", () => {
|
||||
it("does not raise exception", () => {
|
||||
expect(() => { validateKubeConfig(kc, "invalidCluster", { validateCluster: false });}).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("with validateUser as false", () => {
|
||||
describe("with invalid user object", () => {
|
||||
it("does not raise excpetions", () => {
|
||||
expect(() => { validateKubeConfig(kc, "invalidUser", { validateUser: false });}).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("with validateExec as false", () => {
|
||||
describe("with invalid exec object", () => {
|
||||
it("does not raise excpetions", () => {
|
||||
expect(() => { validateKubeConfig(kc, "invalidExec", { validateExec: false });}).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user