Skip to content

Commit

Permalink
Update Independent Mode Installation flow
Browse files Browse the repository at this point in the history
  • Loading branch information
bipuladh committed May 7, 2020
1 parent 02ae207 commit 0a82b98
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 196 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
position: absolute;
opacity: 0;
border: none;
max-width: 4em;
min-height: 2.5em;
max-width: 5.5em;
cursor: grab;
}

.upload-component__textInput {
color: var(--pf-global--Color--dark-200);
color: var(--pf-global--Color--dark-200);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
margin-left: var(--pf-global--spacer--lg);
margin-right: var(--pf-global--spacer--lg);
margin-bottom: var(--pf-global--spacer--md);
&--error {
color: var(--pf-chart-global--danger--Color--100);
}
}

.im-install-page__form {
Expand All @@ -26,6 +29,12 @@
justify-content: space-between;
}

.im-install-page--error {
color: var(--pf-chart-global--danger--Color--100);
.im-install-page__input-box {
background-color: #d8d8d8;
color: #5d5d5d;
}

.im-install-page__text-box {
min-height: 12em;
color: #8e8e8e;
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
import * as React from 'react';
import * as _ from 'lodash';
import { match } from 'react-router';
import {
ButtonBar,
withHandlePromise,
HandlePromiseProps,
} from '@console/internal/components/utils';
import { k8sGet, k8sCreate, referenceForModel } from '@console/internal/module/k8s';
import { k8sGet, k8sCreate, referenceForModel, k8sKill } from '@console/internal/module/k8s';
import { ClusterServiceVersionModel } from '@console/operator-lifecycle-manager';
import { Title, FormGroup, Form, ActionGroup, Button, TextInput } from '@patternfly/react-core';
import {
Title,
FormGroup,
Form,
ActionGroup,
Button,
TextInput,
InputGroup,
Alert,
TextArea,
} from '@patternfly/react-core';
import { history } from '@console/internal/components/utils/router';
import { SecretModel, ConfigMapModel } from '@console/internal/models';
import { SecretModel } from '@console/internal/models';
import { getName } from '@console/shared';
import { OCSServiceModel } from '../../models';
import FileUpload from './fileUpload';
import { DataState, ErrorType, Field } from './types';
import { getValidJSON, checkError } from './utils';
import { isValidJSON, checkError, prettifyJSON } from './utils';
import './install.scss';

const ERROR: DataState = {
clusterName: '',
fsid: '',
admin: '',
monData: '',
};

const getErrorText = (text: string) => <span className="im-install-page--error">{text}</span>;

const InstallExternalCluster = withHandlePromise((props: InstallExternalClusterProps) => {
const {
inProgress,
Expand All @@ -35,39 +34,11 @@ const InstallExternalCluster = withHandlePromise((props: InstallExternalClusterP
match: {
params: { ns, appName },
},
minKeys: { encodedKeys, plainKeys },
} = props;
const [clusterServiceVersion, setClusterServiceVersion] = React.useState(null);
const [fileData, setFileData] = React.useState('');
const [clusterName, setClusterName] = React.useState(null);
const [externalFSID, setExternalFSID] = React.useState(null);
const [externalAdminSecret, setExternalAdminSecret] = React.useState(null);
const [externalMonData, setExternalMonData] = React.useState(null);
const [dataError, setDataError] = React.useState(ERROR);
const [fileError, setFileError] = React.useState('');
const [, updateState] = React.useState();

// Todo(bipuladh): React does shallow comparison dataError and fileError need deep comparison.
const forceUpdate = React.useCallback(() => updateState({}), []);

const setErrors = React.useCallback((errors: ErrorType[]): void => {
for (const err of errors) {
setDataError(Object.assign(ERROR, { [err.field]: err.message }));
}
}, []);

const getState = React.useCallback(
() => ({
[Field.CLUSTER_NAME]: clusterName,
[Field.FSID]: externalFSID,
[Field.ADMIN]: externalAdminSecret,
[Field.MONDATA]: externalMonData,
}),
[externalAdminSecret, externalFSID, externalMonData, clusterName],
);

React.useEffect(() => {
setErrors(checkError(getState()));
}, [getState, setErrors]);
const [dataError, setDataError] = React.useState('');

// File Upload handler
const onUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -76,42 +47,33 @@ const InstallExternalCluster = withHandlePromise((props: InstallExternalClusterP
const file = event.target.files[0];
reader.onload = (ev) => {
const data = ev.target?.result as string;
if (isValidJSON(data)) {
setDataError(checkError(data, plainKeys, encodedKeys));
} else {
setDataError('The uploaded file is not a valid JSON file');
}
setFileData(data);
};
reader.readAsText(file);
};

const onSubmit = (event) => {
event.preventDefault();
// https://github.com/rook/rook/blob/master/cluster/examples/kubernetes/ceph/import-external-cluster.sh

const secret = {
apiVersion: SecretModel.apiVersion,
kind: SecretModel.kind,
metadata: {
name: 'rook-ceph-mon',
name: 'rook-ceph-external-cluster-details',
namespace: ns,
},
stringData: {
'cluster-name': clusterName,
fsid: externalFSID,
'admin-secret': externalAdminSecret,
'mon-secret': 'mon-secret',
// eslint-disable-next-line @typescript-eslint/camelcase
external_cluster_details: fileData,
},
type: 'Opaque',
};
const cmap = {
apiVersion: ConfigMapModel.apiVersion,
kind: ConfigMapModel.kind,
metadata: {
name: 'rook-ceph-mon-endpoints',
namespace: ns,
},
data: {
data: `${externalMonData}`,
mapping: '{}',
maxMonId: '2',
},
};

const ocsObj = {
apiVersion: 'ocs.openshift.io/v1',
kind: OCSServiceModel.kind,
Expand All @@ -126,13 +88,7 @@ const InstallExternalCluster = withHandlePromise((props: InstallExternalClusterP
},
};

handlePromise(
Promise.all([
k8sCreate(SecretModel, secret),
k8sCreate(ConfigMapModel, cmap),
k8sCreate(OCSServiceModel, ocsObj),
]),
)
handlePromise(Promise.all([k8sCreate(SecretModel, secret), k8sCreate(OCSServiceModel, ocsObj)]))
.then((data) => {
history.push(
`/k8s/ns/${ns}/clusterserviceversions/${getName(
Expand All @@ -143,40 +99,15 @@ const InstallExternalCluster = withHandlePromise((props: InstallExternalClusterP
.catch((e) => {
// eslint-disable-next-line no-console
console.error(e);
// Remove secret if cluster creation was not possible
handlePromise(k8sKill(SecretModel, secret));
});
};

const onCancel = () => {
history.goBack();
};

const mapValidDataToState = (json: DataState) => {
const { clusterName: clusterName_, fsid, admin, monData } = json;
setClusterName(clusterName_);
setExternalFSID(fsid);
setExternalAdminSecret(admin);
setExternalMonData(monData);
};

const validate = React.useCallback(() => {
if (!_.isEmpty(fileData)) {
const data = getValidJSON(fileData);
if (data.isValid) {
mapValidDataToState(data.parsedData);
setFileError(null);
} else {
setFileError(data.errorMessage);
}
}
setErrors(checkError(getState()));
}, [fileData, getState, setErrors]);

// File Data validator
React.useEffect(() => {
validate();
forceUpdate();
}, [validate, setFileData, forceUpdate]);

React.useEffect(() => {
k8sGet(ClusterServiceVersionModel, appName, ns)
.then((clusterServiceVersionObj) => {
Expand All @@ -190,71 +121,37 @@ const InstallExternalCluster = withHandlePromise((props: InstallExternalClusterP
<div className="im-install-page">
<div className="im-install-page__sub-header">
<Title size="lg" headingLevel="h5" className="nb-bs-page-title__main">
<div className="im-install-page-sub-header__title">
Connect to external cluster
<FileUpload onUpload={onUpload} />
</div>
<div className="im-install-page-sub-header__title">Connect to external cluster</div>
</Title>
<p className="im--light">
Run &lsaquo;utility-name&rsaquo; to obtain metadata needed for connecting to the
external cluster.
Contact RHCS admin to obtain metadata needed for connecting to the external cluster.
</p>
<Alert
className="co-alert"
variant="info"
title="A bucket will be created to provide the OCS Service."
isInline
/>
</div>
<Form className="im-install-page__form" onSubmit={onSubmit}>
<FormGroup
label="Cluster Name"
isRequired
fieldId="namespace-dropdown"
helperText={getErrorText(dataError.clusterName)}
>
<TextInput
onChange={(val) => setClusterName(val)}
value={clusterName}
aria-label="Enter Cluster name"
placeholder="openshift-storage"
/>
</FormGroup>
<FormGroup
label="External FSID"
isRequired
fieldId="ext-fsid"
helperText={getErrorText(dataError.fsid)}
>
<TextInput
onChange={(val) => setExternalFSID(val)}
value={externalFSID ?? ''}
aria-label="Enter External FSID"
placeholder="asdf-ghjk-qwer-tyui"
/>
</FormGroup>
<FormGroup
label="External admin secret"
isRequired
fieldId="ext-admin"
helperText={getErrorText(dataError.admin)}
>
<TextInput
onChange={(val) => setExternalAdminSecret(val)}
value={externalAdminSecret ?? ''}
aria-label="Enter Admin secret"
placeholder="!123jakajs==djjzla2"
/>
<FormGroup label="External cluster metadata" isRequired fieldId="cluster-metadata">
<InputGroup>
<TextInput
aria-label="Upload JSON File"
value="Upload Credentials file"
className="im-install-page__input-box"
/>
<FileUpload onUpload={onUpload} />
</InputGroup>
</FormGroup>
<FormGroup
label="External mon data"
isRequired
fieldId="ext-mon-data"
helperText={getErrorText(dataError.monData)}
>
<TextInput
onChange={(val) => setExternalMonData(val)}
value={externalMonData ?? ''}
aria-label="Enter Mon Data"
placeholder="a='12.22.123.22'"
<FormGroup fieldId="preview-box">
<TextArea
value={prettifyJSON(fileData)}
className="im-install-page__text-box"
isValid={!dataError}
/>
</FormGroup>

<ButtonBar errorMessage={fileError || errorMessage} inProgress={inProgress}>
<ButtonBar errorMessage={dataError || errorMessage} inProgress={inProgress}>
<ActionGroup>
<Button type="submit" variant="primary">
Create
Expand All @@ -272,6 +169,7 @@ const InstallExternalCluster = withHandlePromise((props: InstallExternalClusterP

type InstallExternalClusterProps = HandlePromiseProps & {
match: match<{ ns?: string; appName?: string }>;
minKeys?: { [key: string]: string[] };
};

export default InstallExternalCluster;

This file was deleted.

0 comments on commit 0a82b98

Please sign in to comment.