Skip to content

Commit 638aba1

Browse files
authoredNov 27, 2020
Merge pull request #134 from kinvolk/show_errors_when_no_cluster_is_configured
Show errors when no cluster is configured
2 parents bbc30ef + b891b4b commit 638aba1

File tree

7 files changed

+65
-30
lines changed

7 files changed

+65
-30
lines changed
 

‎backend/cmd/headlamp.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func StartHeadlampServer(config *HeadlampConfig) {
9191
if config.useInCluster {
9292
context, err := GetOwnContext(config)
9393
if err != nil {
94-
log.Fatal("Failed to get in-cluster config", err)
94+
log.Println("Failed to get in-cluster config", err)
9595
}
9696

9797
contexts = append(contexts, *context)
@@ -103,7 +103,7 @@ func StartHeadlampServer(config *HeadlampConfig) {
103103

104104
contextsFound, err := GetContextsFromKubeConfigFile(kubeConfigPath)
105105
if err != nil {
106-
log.Fatal("Failed to get contexts from", kubeConfigPath, err)
106+
log.Println("Failed to get contexts from", kubeConfigPath, err)
107107
}
108108

109109
contexts = append(contexts, contextsFound...)

‎frontend/src/App.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ function AuthRoute(props: AuthRouteProps) {
285285
if (requiresCluster) {
286286
const clusterName = getCluster();
287287
if (!!clusterName) {
288-
const cluster = clusters[clusterName];
288+
const cluster = clusters ? clusters[clusterName] : undefined;
289289
const requiresToken = (cluster?.useToken === undefined || cluster?.useToken);
290290
if (!!getToken(clusterName) || !requiresToken) {
291291
return children;

‎frontend/src/components/account/Auth.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ function Auth() {
4646
disableBackdropClick
4747
>
4848
<DialogTitle id="responsive-dialog-title">
49-
{Object.keys(clusterConf).length > 1 ? `Authentication: ${getCluster()}` : 'Authentication'}
49+
{Object.keys(clusterConf || {}).length > 1 ? `Authentication: ${getCluster()}` : 'Authentication'}
5050
</DialogTitle>
5151
<DialogContent>
5252
<DialogContentText>
@@ -64,7 +64,7 @@ function Auth() {
6464
/>
6565
</DialogContent>
6666
<DialogActions>
67-
{Object.keys(clusters).length > 1 &&
67+
{Object.keys(clusters || {}).length > 1 &&
6868
<>
6969
<Button onClick = {() => history.replace('/')} color="primary">
7070
Cancel

‎frontend/src/components/authchooser/index.tsx

+9-7
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function AuthChooser(){
4242
const clusterName = getCluster() as string;
4343

4444
let clusterAuthType = '';
45-
if (clusters[clusterName]) {
45+
if (clusters && clusters[clusterName]) {
4646
clusterAuthType = clusters[clusterName].auth_type;
4747
}
4848

@@ -64,7 +64,7 @@ function AuthChooser(){
6464

6565
React.useEffect(() => {
6666
const clusterName = getCluster();
67-
if (!clusterName || testingAuth || Object.keys(clusters).length === 0) {
67+
if (!clusterName || testingAuth || !clusters || Object.keys(clusters).length === 0) {
6868
return;
6969
}
7070

@@ -156,12 +156,14 @@ function AuthChooser(){
156156
<ColorButton onClick={handleTokenAuth}>Use A Token</ColorButton>
157157
</Box>
158158
</Box>
159-
<Box m={2} display="flex" alignItems="center" style={{cursor: 'pointer'}} onClick={handleBackButtonPress}>
160-
<Box pt={0.5}>
161-
<InlineIcon icon={chevronLeft} height={20} width={20}/>
159+
{!!clusters && Object.keys(clusters).length > 1 &&
160+
<Box m={2} display="flex" alignItems="center" style={{cursor: 'pointer'}} onClick={handleBackButtonPress}>
161+
<Box pt={0.5}>
162+
<InlineIcon icon={chevronLeft} height={20} width={20}/>
163+
</Box>
164+
<Box fontSize={14}>BACK</Box>
162165
</Box>
163-
<Box fontSize={14}>BACK</Box>
164-
</Box>
166+
}
165167
</Box>
166168
}
167169
</ClusterDialog>

‎frontend/src/components/cluster/Chooser.tsx

+20-7
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export function ClusterTitle() {
4545
return null;
4646
}
4747

48-
if (Object.keys(clusters).length <= 1) {
48+
if (Object.keys(clusters || {}).length <= 1) {
4949
return null;
5050
}
5151

@@ -230,7 +230,7 @@ function Chooser(props: ClusterDialogProps) {
230230

231231
// If we only have one cluster configured, then we skip offering
232232
// the choice to the user.
233-
if (Object.keys(clusters).length === 1) {
233+
if (!!clusters && Object.keys(clusters).length === 1) {
234234
handleButtonClick(Object.values(clusters)[0]);
235235
}
236236
},
@@ -259,7 +259,7 @@ function Chooser(props: ClusterDialogProps) {
259259
}
260260
}
261261

262-
const clusterList = Object.values(clusters);
262+
const clusterList = Object.values(clusters || {});
263263

264264
return (
265265
<ClusterDialog
@@ -269,10 +269,23 @@ function Chooser(props: ClusterDialogProps) {
269269
>
270270
{clusterList.length === 0 ?
271271
<React.Fragment>
272-
<DialogContentText>
273-
Wait while fetching clusters…
274-
</DialogContentText>
275-
<Loader />
272+
{clusters === null ?
273+
<>
274+
<DialogContentText>
275+
Wait while fetching clusters…
276+
</DialogContentText>
277+
<Loader />
278+
</>
279+
:
280+
<>
281+
<DialogContentText>
282+
There seems to be no clusters configured…
283+
</DialogContentText>
284+
<DialogContentText>
285+
Please make sure you have at least one cluster configured.
286+
</DialogContentText>
287+
</>
288+
}
276289
</React.Fragment>
277290
:
278291
<React.Fragment>

‎frontend/src/lib/k8s/index.ts

+29-9
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import ServiceAccount from './serviceAccount';
3030
import StatefulSet from './statefulSet';
3131
import StorageClass from './storageClass';
3232

33+
const CLUSTER_FETCH_INTERVAL = 60 * 1000; // ms
34+
3335
const classList = [
3436
ClusterRole,
3537
ClusterRoleBinding,
@@ -73,30 +75,48 @@ interface Config {
7375
}
7476

7577
// Hook for getting or fetching the clusters configuration.
76-
export function useClustersConf() {
78+
export function useClustersConf(): ConfigState['clusters'] {
7779
const dispatch = useDispatch();
7880
const clusters = useTypedSelector(state => state.config.clusters);
81+
const [retry, setRetry] = React.useState(!clusters || Object.keys(clusters).length === 0);
7982

8083
React.useEffect(() => {
81-
if (Object.keys(clusters).length === 0) {
84+
let retryHandler = 0;
85+
86+
if (retry) {
87+
setRetry(false);
88+
8289
request('/config', {}, false, false)
8390
.then((config: Config) => {
84-
const clusters: ConfigState['clusters'] = {};
91+
const clustersToConfig: ConfigState['clusters'] = {};
8592
config?.clusters.forEach((cluster: Cluster) => {
86-
clusters[cluster.name] = cluster;
93+
clustersToConfig[cluster.name] = cluster;
8794
});
8895

8996
const configToStore = {
9097
...config,
91-
clusters
98+
clusters: clustersToConfig
9299
};
93-
dispatch(setConfig(configToStore));
100+
101+
if (!clusters || Object.keys(clusters).length !== 0 ||
102+
Object.keys(clustersToConfig).length !== 0) {
103+
dispatch(setConfig(configToStore));
104+
}
94105
})
95-
.catch((err: Error) => console.error(err));
96-
return;
106+
.catch((err: Error) => {
107+
console.error(err);
108+
retryHandler = window.setInterval(() => setRetry(true), CLUSTER_FETCH_INTERVAL);
109+
});
110+
111+
return function cleanup() {
112+
if (retryHandler !== 0) {
113+
window.clearInterval(retryHandler);
114+
retryHandler = 0;
115+
}
116+
};
97117
}
98118
},
99-
[clusters, dispatch]);
119+
[clusters, dispatch, retry]);
100120

101121
return clusters;
102122
}

‎frontend/src/redux/reducers/config.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import { Action, CONFIG_NEW } from '../actions/actions';
44
export interface ConfigState {
55
clusters: {
66
[clusterName: string]: Cluster;
7-
};
7+
} | null;
88
}
99

1010
export const INITIAL_STATE: ConfigState = {
11-
clusters: {},
11+
clusters: null,
1212
};
1313

1414
function reducer(state = INITIAL_STATE, action: Action) {

0 commit comments

Comments
 (0)
Please sign in to comment.