Skip to content

Commit 9c29059

Browse files
Merge branch 'main' into reduce-devfile-registry-odo-call
2 parents 5f6339d + cb693cd commit 9c29059

File tree

6 files changed

+225
-32
lines changed

6 files changed

+225
-32
lines changed

src/util/kubeUtils.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,61 @@
33
* Licensed under the MIT License. See LICENSE file in the project root for license information.
44
*-----------------------------------------------------------------------------------------------*/
55

6+
import * as fs from 'fs';
7+
import * as path from 'path';
68
import { QuickPickItem } from 'vscode';
7-
import { KubeConfig } from '@kubernetes/client-node';
9+
import { KubeConfig, findHomeDir, loadYaml } from '@kubernetes/client-node';
810
import { User, Cluster } from '@kubernetes/client-node/dist/config_types';
911

12+
function fileExists(file: string): boolean {
13+
try {
14+
fs.accessSync(file);
15+
return true;
16+
} catch ( _ ) {
17+
return false;
18+
}
19+
}
20+
1021
export class KubeConfigUtils extends KubeConfig {
1122
constructor() {
1223
super();
1324
this.loadFromDefault();
25+
// k8s nodejs-client ignores all unknown properties,
26+
// so cluster object's proxy-url attribute is not present
27+
// after k8s config loaded
28+
}
29+
30+
findHomeDir() {
31+
return findHomeDir();
32+
}
33+
34+
getProxy(contextName: string): string | undefined {
35+
if (process.env.KUBECONFIG?.[1]) {
36+
const cFiles = process.env.KUBECONFIG.split(path.delimiter).filter(file => file);
37+
//const yaml =
38+
for (let i=0; i < cFiles.length; i++) {
39+
const proxyUrl = this.getClusterProxyFromFile(cFiles[i], contextName);
40+
if (proxyUrl) return proxyUrl;
41+
}
42+
return;
43+
}
44+
const home = this.findHomeDir();
45+
if (home) {
46+
const config = path.join(home, '.kube', 'config');
47+
if (fileExists(config)) {
48+
return this.getClusterProxyFromFile(config, contextName);
49+
}
50+
}
51+
}
52+
53+
getClusterProxyFromFile(file: string, contextName: string): string {
54+
const fileContent = fs.readFileSync(file, 'utf8');
55+
const yaml: any = loadYaml(fileContent);
56+
const contextObj = yaml.contexts.find(
57+
(context) => context.name === contextName);
58+
const clusterObj = yaml.clusters.find(
59+
(cluster) => cluster.name === contextObj?.context?.cluster);
60+
return clusterObj?.cluster?.['proxy-url'];
1461
}
1562

1663
public getServers(): QuickPickItem[] {

src/webview/cluster/app/cluster.style.tsx

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ export default (theme: Theme) =>
3535
},
3636
cardTransform: {
3737
width: '27em',
38+
height: 'auto',
3839
marginRight: theme.spacing(4),
39-
position: 'relative',
4040
transform: 'scale(0.95)',
4141
'&:hover': {
4242
transform: 'scale(1)',
@@ -48,18 +48,50 @@ export default (theme: Theme) =>
4848
padding: theme.spacing(2),
4949
borderBottom: '0 solid transparent'
5050
},
51+
cardImageContainer: {
52+
display: 'inherit',
53+
padding: '10px',
54+
height: '50px',
55+
maxHeight: '10rem',
56+
maxWidth: '15rem'
57+
},
58+
cardImageTableContainer: {
59+
display: 'inline-block',
60+
verticalAlign: 'top',
61+
height: '15%',
62+
width: 'auto',
63+
marginTop: '1rem'
64+
},
65+
cardBody: {
66+
maxHeight: '15em',
67+
overflow: 'hidden',
68+
overflowY:'scroll',
69+
'&::-webkit-scrollbar':{
70+
width:0,
71+
}
72+
},
73+
cardBodyMargin: {
74+
marginTop: theme.spacing(3)
75+
},
5176
cardButton: {
5277
display: 'block',
53-
marginBottom: theme.spacing(2)
78+
margin: theme.spacing(2)
5479
},
5580
button: {
81+
display: 'table-cell',
82+
minWidth: '8rem',
83+
maxWidth: '20rem',
84+
maxHeight: '5rem',
85+
height: '2rem',
86+
textAlign: 'center',
87+
overflow: 'hidden',
88+
textOverflow: 'ellipsis',
5689
color: 'var(--vscode-button-foreground)',
5790
backgroundColor: '#EE0000',
5891
'&:hover': {
59-
color: 'var(--vscode-button-foreground)',
6092
backgroundColor: '#BE0000',
6193
},
62-
textTransform: "none"
94+
textTransform: 'none'
6395
},
6496
cardContent: {
6597
background: 'var(--vscode-settings-focusedRowBackground)',
@@ -70,4 +102,4 @@ export default (theme: Theme) =>
70102
maxHeight: '100%',
71103
maxWidth: '100%'
72104
}
73-
})
105+
})

src/webview/cluster/app/cluster.tsx

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
Tooltip,
1717
Typography
1818
} from '@material-ui/core';
19-
19+
import clsx from 'clsx';
2020
import AddClusterView from './clusterView';
2121
import AddSandboxView from './sandboxView';
2222
import clusterStyle from './cluster.style';
@@ -108,35 +108,39 @@ export default function Header() {
108108
</Typography>
109109
</div>
110110
<CardContent style={{ height: 240 }}>
111-
<Typography style={{ padding: '10px', height: '50px' }}>
111+
<Typography className={index === 2 ? classes.cardImageTableContainer : classes.cardImageContainer}>
112112
{list.imageUrl.map((url: string, index: string | number) => (
113-
<img src={url} key={index} className={classes.image} style={{ marginLeft: '.625rem', marginRight: '.625rem' }}></img>
113+
<img src={url} key={index} className={classes.image} style={{ marginLeft: '.625rem', marginRight: '.625rem', position: 'relative' }}></img>
114114
))}
115115
</Typography>
116-
<List>
117-
<ListItem>
118-
<ListItemText
119-
primary={list.description}
120-
secondary={list.smallInfo} />
121-
</ListItem>
122-
</List>
116+
<div className={index === 2 ? clsx(classes.cardBody, classes.cardBodyMargin) : classes.cardBody}>
117+
<List>
118+
<ListItem>
119+
<ListItemText
120+
primary={list.description}
121+
secondary={list.smallInfo} />
122+
</ListItem>
123+
</List>
124+
</div>
123125
</CardContent>
124-
<CardActions className={classes.cardButton}>
125-
<Tooltip title={list.tooltip} placement="top">
126-
<div>
127-
<a onClick={() => handleView(index)} style={{ textDecoration: 'none' }} href={clusterTypes[index].redirectLink || '#'}>
128-
<Button
129-
variant="contained"
130-
color="default"
131-
component="span"
132-
className={classes.button}
133-
>
134-
{list.buttonText}
135-
</Button>
136-
</a>
137-
</div>
138-
</Tooltip>
139-
</CardActions>
126+
<div>
127+
<CardActions className={classes.cardButton}>
128+
<Tooltip title={list.tooltip} placement="top">
129+
<div>
130+
<a onClick={() => handleView(index)} style={{ textDecoration: 'none' }} href={clusterTypes[index].redirectLink || '#'}>
131+
<Button
132+
variant="contained"
133+
color="default"
134+
component="span"
135+
className={classes.button}
136+
>
137+
{list.buttonText}
138+
</Button>
139+
</a>
140+
</div>
141+
</Tooltip>
142+
</CardActions>
143+
</div>
140144
</Card>
141145
))}
142146
</>

test/fixtures/.kube/config

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
apiVersion: v1
2+
kind: Config
3+
clusters:
4+
- cluster:
5+
proxy-url: socks5://localhost:1080
6+
server: https://cluster1
7+
name: cluster1
8+
- cluster:
9+
proxy-url: http://localhost:1081
10+
server: https://cluster2
11+
name: cluster2
12+
- cluster:
13+
proxy-url: https://localhost:1080
14+
server: https://cluster3
15+
name: cluster3
16+
- cluster:
17+
server: https://cluster4
18+
name: cluster4
19+
contexts:
20+
- context:
21+
cluster: cluster1
22+
user: cluster1user
23+
name: context-cluster1
24+
- context:
25+
cluster: cluster2
26+
user: cluster2user
27+
name: context-cluster2
28+
- context:
29+
cluster: cluster3
30+
user: cluster3user
31+
name: context-cluster3
32+
- context:
33+
cluster: cluster4
34+
user: cluster4user
35+
name: context-cluster4
36+
current-context: cluster1
37+
preferences: {}
38+
users:
39+
- name: cluster1user
40+
user:
41+
token: token1
42+
- name: cluster2user
43+
user:
44+
token: token2
45+
- name: cluster3user
46+
user:
47+
token: token3
48+
- name: cluster4user
49+
user:
50+
token: token4

test/fixtures/.kube/config1

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: v1
2+
kind: Config
3+
clusters:
4+
- cluster:
5+
proxy-url: http://localhost:1081
6+
server: https://cluster4
7+
name: cluster5
8+
contexts:
9+
- context:
10+
cluster: cluster5
11+
user: cluster5user
12+
name: context-cluster5
13+
current-context: cluster5
14+
preferences: {}
15+
users:
16+
- name: cluster5user
17+
user:
18+
token: token5

test/unit/util/kubeUtils.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*-----------------------------------------------------------------------------------------------
2+
* Copyright (c) Red Hat, Inc. All rights reserved.
3+
* Licensed under the MIT License. See LICENSE file in the project root for license information.
4+
*-----------------------------------------------------------------------------------------------*/
5+
6+
import * as chai from 'chai';
7+
import * as sinonChai from 'sinon-chai';
8+
import * as sinon from 'sinon';
9+
import path = require('path');
10+
import { KubeConfigUtils } from '../../../src/util/kubeUtils';
11+
12+
const {expect} = chai;
13+
chai.use(sinonChai);
14+
15+
suite('K8s Configuration Utility', () => {
16+
let sandbox: sinon.SinonSandbox;
17+
const homeDir = path.resolve(__dirname, '..', '..', '..', '..', 'test', 'fixtures');
18+
const configDir = path.resolve(homeDir,'.kube');
19+
20+
setup(() => {
21+
sandbox = sinon.createSandbox();
22+
});
23+
24+
teardown(() => {
25+
sandbox.restore();
26+
});
27+
28+
test('loads configs listed in KUBECONFIG evn variable', () => {
29+
sandbox.stub(process, 'env').value({
30+
'KUBECONFIG': [path.join(configDir, 'config'), path.join(configDir, 'config1')].join(path.delimiter)
31+
});
32+
const kc = new KubeConfigUtils();
33+
expect(kc.getProxy('context-cluster5')).is.not.undefined;
34+
expect(kc.getProxy('context-cluster4')).is.undefined;
35+
});
36+
37+
test('loads ~/.kube/config', () => {
38+
sandbox.stub(KubeConfigUtils.prototype, 'findHomeDir').returns(homeDir);
39+
const kc = new KubeConfigUtils();
40+
expect(kc.getProxy('context-cluster1')).is.not.undefined;
41+
})
42+
});

0 commit comments

Comments
 (0)