Skip to content

Commit

Permalink
Merge pull request #4215 from kubeshop/refactor/debugging-failed-clus…
Browse files Browse the repository at this point in the history
…ter-setup

refactor/debugging failed cluster setup
  • Loading branch information
monojack authored Feb 26, 2024
2 parents 7cf8888 + 8508bc1 commit aff91f9
Show file tree
Hide file tree
Showing 11 changed files with 61 additions and 9 deletions.
8 changes: 8 additions & 0 deletions electron/app/services/cluster/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ const errors = createErrors({
title: 'Cannot connect to the cluster',
description: 'There is no current context selected.',
},
'proxy-missing-context': {
title: 'Cannot connect to the cluster',
description: 'The specified context does not exist. Please check your kubeconfig file.',
},
'proxy-invalid-config': {
title: 'Cannot connect to the cluster',
description: 'The proxy connection arguments were invalid.',
},
'local-connection-refused': {
title: 'Cannot connect to the cluster',
description: 'The connection was refused - is your Docker Engine or VM running?',
Expand Down
3 changes: 3 additions & 0 deletions electron/app/services/cluster/handlers/debugProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export async function debugProxy({context, kubeconfig}: DebugProxyArgs): Promise
return proxy.debugInfo;
}

const proxy = await PROXY_SERVICE.getLast();
if (proxy) return proxy.debugInfo;

return {
cmd: 'kubectl proxy exited',
logs: [
Expand Down
6 changes: 6 additions & 0 deletions electron/app/services/cluster/handlers/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ function determineError(reason: string, contextId: ContextId): MonokleClusterErr
if (reason === 'MONOKLE_PROXY_EMPTY_CONTEXT') {
return getMonokleClusterError('proxy-empty-context', contextId);
}
if (reason === 'MONOKLE_PROXY_MISSING_CONTEXT') {
return getMonokleClusterError('proxy-missing-context', contextId);
}
if (reason === 'MONOKLE_PROXY_INVALID_CONFIG') {
return getMonokleClusterError('proxy-invalid-config', contextId);
}

// Kubectl user authentication error.
// These happen within the local kube-proxy.
Expand Down
8 changes: 6 additions & 2 deletions electron/kubernetes/ProxyInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ export class ProxyInstance {
throw new Error('MONOKLE_PROXY_EMPTY_CONTEXT');
}

const globalOptions = [`--context=${this.context}`];
if (this.kubeconfig) globalOptions.push(`--kubeconfig=${this.kubeconfig}`);
const globalOptions = [`--context=${JSON.stringify(this.context)}`];
if (this.kubeconfig) globalOptions.push(`--kubeconfig=${JSON.stringify(this.kubeconfig)}`);
if (this.verbosity) globalOptions.push(`-v=${this.verbosity}`);

const proxyOptions = [`--port=${this.port}`];
Expand Down Expand Up @@ -113,6 +113,10 @@ export class ProxyInstance {
proxySignal.reject(new Error('EADDRINUSE'));
} else if (msg.includes('error: The gcp auth plugin has been removed')) {
proxySignal.reject(new Error('MONOKLE_PROXY_GCP_LEGACY_PLUGIN'));
} else if (/^error: flags cannot be placed before/i.test(msg)) {
proxySignal.reject(new Error('MONOKLE_PROXY_INVALID_CONFIG'));
} else if (/^(error: context).*(does not exist)/i.test(msg)) {
proxySignal.reject(new Error('MONOKLE_PROXY_MISSING_CONTEXT'));
} else {
// do nothing and let the timeout reject eventually.
// For instance, high verbosity logs plenty of details
Expand Down
6 changes: 6 additions & 0 deletions electron/kubernetes/ProxyService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const PROXY_MAX_ATTEMPTS = 25;
export class ProxyService {
private nextPort = 30001;
private proxies: ProxyInstance[] = [];
private last: ProxyInstance | undefined;

get(context: string, kubeconfig?: string): Promise<ProxyInstance> {
const proxy = this.proxies.find(p => p.context === context && p.kubeconfig === kubeconfig);
Expand All @@ -21,6 +22,10 @@ export class ProxyService {
return this.start(context, kubeconfig);
}

async getLast(): Promise<ProxyInstance | undefined> {
return this.last;
}

find(context: string) {
return this.proxies.find(p => p.context === context);
}
Expand All @@ -34,6 +39,7 @@ export class ProxyService {
this.nextPort += 1;

const proxy = new ProxyInstance({context, kubeconfig, port, verbosity: undefined});
this.last = proxy;
await proxy.start();

proxy.onDelete = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import styled from 'styled-components';
import {Tooltip} from '@components/atoms/Tooltip/Tooltip';

import {useMainPaneDimensions} from '@utils/hooks';
import {preserveControlCharacters} from '@utils/preserveControlCharacters';

import {ContextId} from '@shared/ipc';
import {Colors} from '@shared/styles';
Expand Down Expand Up @@ -77,12 +78,17 @@ export function DebugClusterDrawer({contextId, open, onClose}: Props) {
<div />

<div style={{paddingBottom: 8}}>
{Boolean(data?.cmd) && (
<CodeBlock>
<pre>{data?.cmd}</pre>
</CodeBlock>
)}
{logs.map(l => (
<LogEntry key={l.timestamp}>
<LogMeta>
[{DateTime.fromMillis(l.timestamp).toFormat('HH:MM:ss')} - {l.type}]
</LogMeta>
<LogContent $wrap={wordWrap}>{l.content}</LogContent>
<LogContent $wrap={wordWrap}>{preserveControlCharacters(l.content)}</LogContent>
</LogEntry>
))}
</div>
Expand Down Expand Up @@ -135,3 +141,17 @@ const ButtonBox = styled.div<{$wrap: boolean}>`
color: ${Colors.lightSeaGreen};
}
`;

const CodeBlock = styled.code`
> pre {
width: min-content;
max-width: 100%;
overflow: auto;
display: block;
white-space: no-wrap;
padding: 8px;
border-radius: 4px;
background: ${Colors.grey3b};
color: ${Colors.grey8};
}
`;
4 changes: 2 additions & 2 deletions src/redux/cluster/service/kube-control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const KUBECTL = {

function createGlobalArgs(globals: KubectlGlobal) {
const globalArgs = [];
if (globals.kubeconfig) globalArgs.push(`--kubeconfig=${globals.kubeconfig}`);
if (globals.context) globalArgs.push(`--context=${globals.context}`);
if (globals.kubeconfig) globalArgs.push(`--kubeconfig=${JSON.stringify(globals.kubeconfig)}`);
if (globals.context) globalArgs.push(`--context=${JSON.stringify(globals.context)}`);
return globalArgs;
}
2 changes: 1 addition & 1 deletion src/redux/thunks/applyResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function applyKustomization(
) {
const folder = getAbsoluteResourceFolder(resourceMeta, fileMap);

const args: string[] = ['--context', context];
const args: string[] = ['--context', JSON.stringify(context)];
if (namespace) {
args.push(...['--namespace', namespace.name]);
}
Expand Down
2 changes: 1 addition & 1 deletion src/shared/utils/commands/kubectl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function createKubectlApplyCommand(
{context, namespace, input}: KubectlApplyArgs,
env?: KubectlEnv
): CommandOptions {
const args = ['--context', context, 'apply', '-f', '-'];
const args = ['--context', JSON.stringify(context), 'apply', '-f', '-'];

if (namespace) {
args.unshift('--namespace', namespace);
Expand Down
4 changes: 2 additions & 2 deletions src/utils/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ export function getHelmClusterArgs(): string[] {

const args = [
'--kubeconfig',
kubeconfigPath,
JSON.stringify(kubeconfigPath),
'--kube-context',
context,
JSON.stringify(context),
'--kube-apiserver',
`http://127.0.0.1:${proxyPort}`,
];
Expand Down
5 changes: 5 additions & 0 deletions src/utils/preserveControlCharacters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function preserveControlCharacters(str: string) {
return str.replace(/[\n\r\t]/g, cc => {
return `\\${cc === '\n' ? 'n' : cc === '\r' ? 'r' : 't'}`;
});
}

0 comments on commit aff91f9

Please sign in to comment.