Skip to content

Commit

Permalink
KMS Support for cluster creation
Browse files Browse the repository at this point in the history
Signed-off-by: Kanika Murarka <kmurarka@redhat.com>
  • Loading branch information
Kanika Murarka committed Dec 1, 2020
1 parent 63424de commit 226c6f3
Show file tree
Hide file tree
Showing 18 changed files with 796 additions and 96 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.ocs-install-kms {
&__form-url {
display: inline-flex;
width: 100%;
}

&__form-address {
width: 70%;
}

&__form-port {
width: 30%;
}
}
Original file line number Diff line number Diff line change
@@ -1,67 +1,201 @@
import * as React from 'react';
import { FormGroup, TextInput } from '@patternfly/react-core';
import { State, Action } from '../ocs-install/attached-devices/create-sc/state';
import * as _ from 'lodash';

import {
FormGroup,
TextInput,
FormSelect,
FormSelectOption,
Button,
ValidatedOptions,
} from '@patternfly/react-core';
import { global_palette_blue_300 as blueInfoColor } from '@patternfly/react-tokens/dist/js/global_palette_blue_300';
import { PencilAltIcon } from '@patternfly/react-icons';

import { advancedKMSModal } from '../modals/advanced-kms-modal/advanced-kms-modal';
import {
InternalClusterState,
InternalClusterAction,
ActionType,
} from '../ocs-install/internal-mode/reducer';
import {
setDispatch,
Validation,
VALIDATIONS,
ValidationMessage,
} from '../../utils/common-ocs-install-el';
import { KMSProviders } from '../../constants/ocs-install';
import { KMSConfig } from '../ocs-install/types';
import { State, Action } from '../ocs-install/attached-devices/create-sc/state';
import { setEncryptionDispatch, parseURL } from './utils';

const validate = (valid: boolean): Validation => {
let validation: Validation;
if (!valid) {
validation = VALIDATIONS.REQUIRED_FIELD_KMS;
}
return validation;
};
import './kms-config.scss';

/* @TODO - complete KMS View in follow up PR */
export const KMSConfigure: React.FC<KMSConfigureProps> = ({ state, dispatch, mode }) => {
const { kms } = state;
const validation: Validation = validate(kms.hasHandled);
const [kmsProvider, setKMSProvider] = React.useState<string>(KMSProviders[0].name);

React.useEffect(() => {
if (!kms.name) {
setDispatch(ActionType.SET_KMS_ENCRYPTION, { ...kms, hasHandled: false }, mode, dispatch);
} else {
setDispatch(ActionType.SET_KMS_ENCRYPTION, { ...kms, hasHandled: true }, mode, dispatch);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [kms.name]);
const setServiceName = (name: string) => {
const kmsObj: KMSConfig = kms;
kmsObj.name.value = name;
name !== '' ? (kms.name.valid = true) : (kms.name.valid = false);
kmsObj.hasHandled = kms.name.valid;
setEncryptionDispatch(ActionType.SET_KMS_ENCRYPTION, mode, dispatch, kmsObj);
};

const getServiceName = (name: string) => {
setDispatch(ActionType.SET_KMS_ENCRYPTION, { ...kms, name }, mode, dispatch);
const setAddress = (address: string) => {
const kmsObj: KMSConfig = kms;
kmsObj.address.value = address;
address !== '' && parseURL(address.trim())
? (kms.address.valid = true)
: (kms.address.valid = false);
kmsObj.hasHandled = kms.address.valid;
setEncryptionDispatch(ActionType.SET_KMS_ENCRYPTION, mode, dispatch, kmsObj);
};

const setAddressPort = (port: string) => {
const kmsObj: KMSConfig = kms;
kmsObj.port.value = port;
port !== '' && !_.isNaN(Number(port)) && Number(port) > 0
? (kms.port.valid = true)
: (kms.port.valid = false);
kmsObj.hasHandled = kms.port.valid;
setEncryptionDispatch(ActionType.SET_KMS_ENCRYPTION, mode, dispatch, kmsObj);
};

const setToken = (token: string) => {
const kmsObj: KMSConfig = kms;
kmsObj.token.value = token;
token !== '' ? (kms.token.valid = true) : (kms.token.valid = false);
kmsObj.hasHandled = kms.token.valid;
setEncryptionDispatch(ActionType.SET_KMS_ENCRYPTION, mode, dispatch, kmsObj);
};

const validateAddressMessage = () =>
kms.address.value === '' ? 'This is a required field' : 'Please enter a URL';

const validatePortMessage = () =>
kms.port.value === '' ? 'This is a required field' : 'Please enter a valid port';

const openAdvancedModal = () =>
advancedKMSModal({
state,
dispatch,
mode,
});

const isValid = (value: boolean) => (value ? ValidatedOptions.default : ValidatedOptions.error);

return (
<>
<div className="co-m-pane__form">
<FormGroup
fieldId="kms-provider"
label="Key Management Service Provider"
className="ocs-install-encryption__form-body"
>
<FormSelect
value={kmsProvider}
onChange={setKMSProvider}
id="kms-provider"
name="kms-provider-name"
aria-label="kms-provider-name"
isDisabled
>
{KMSProviders.map((provider) => (
<FormSelectOption value={provider.value} label={provider.name} />
))}
</FormSelect>
</FormGroup>
<FormGroup
fieldId="kms-service-name"
label="Service Name"
className="co-m-pane__form ocs-install-encryption__form-body"
className="ocs-install-encryption__form-body"
helperTextInvalid="This is a required field"
validated={isValid(kms.name.valid)}
isRequired
>
<TextInput
value={kms.name}
onChange={getServiceName}
isRequired
value={kms.name.value}
onChange={setServiceName}
type="text"
id="kms-service-name"
name="kms-service-name"
isRequired
validated={isValid(kms.name.valid)}
/>
{validation && <ValidationMessage validation={validation} />}
</FormGroup>
</>
<div className="ocs-install-kms__form-url">
<FormGroup
fieldId="kms-address"
label="Address"
className="ocs-install-kms__form-address ocs-install-encryption__form-body"
helperTextInvalid={validateAddressMessage()}
validated={isValid(kms.address.valid)}
isRequired
>
<TextInput
value={kms.address.value}
onChange={setAddress}
className="ocs-install-kms__form-address--padding"
type="url"
id="kms-address"
name="kms-address"
isRequired
validated={isValid(kms.address.valid)}
/>
</FormGroup>
<FormGroup
fieldId="kms-address-port"
label="Port"
className="ocs-install-kms__form-port ocs-install-encryption__form-body--small-padding"
helperTextInvalid={validatePortMessage()}
validated={isValid(kms.port.valid)}
isRequired
>
<TextInput
value={kms.port.value}
onChange={setAddressPort}
type="text"
id="kms-address-port"
name="kms-address-port"
isRequired
validated={isValid(kms.port.valid)}
/>
</FormGroup>
</div>
<FormGroup
fieldId="kms-token"
label="Token"
className="ocs-install-encryption__form-body"
helperTextInvalid="This is a required field"
validated={isValid(kms.token.valid)}
isRequired
>
<TextInput
value={kms.token.value}
onChange={setToken}
type="password"
id="kms-token"
name="kms-token"
isRequired
validated={isValid(kms.token.valid)}
/>
</FormGroup>
<Button
variant="link"
className="ocs-install-encryption__form-body"
onClick={openAdvancedModal}
>
Advanced Settings{' '}
{(kms.backend ||
kms.caCert ||
kms.tls ||
kms.clientCert ||
kms.clientKey ||
kms.providerNamespace) && (
<PencilAltIcon data-test="edit-icon" size="sm" color={blueInfoColor.value} />
)}
</Button>
</div>
);
};

type KMSConfigureProps = {
state: State | InternalClusterState;
state: InternalClusterState | State;
dispatch: React.Dispatch<Action | InternalClusterAction>;
mode: string;
mode?: string;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import * as React from 'react';
import * as _ from 'lodash';
import { K8sResourceKind, ConfigMapKind, SecretKind } from '@console/internal/module/k8s/types';
import { k8sCreate } from '@console/internal/module/k8s/resource';
import { ConfigMapModel, SecretModel } from '@console/internal/models';
import { CEPH_STORAGE_NAMESPACE, MODES, KMSConfigMapName, KMSSecretName } from '../../constants';
import { Action } from '../ocs-install/attached-devices/create-sc/state';
import { InternalClusterAction } from '../ocs-install/internal-mode/reducer';
import { KMSConfig, KMSConfigMap } from '../ocs-install/types';

export const parseURL = (url: string) => {
try {
return new URL(url);
} catch (e) {
return null;
}
};

export const createKmsResources = (kms: KMSConfig) => {
const tokenSecret: SecretKind = {
apiVersion: SecretModel.apiVersion,
kind: SecretModel.kind,
metadata: {
name: KMSSecretName,
namespace: CEPH_STORAGE_NAMESPACE,
},
stringData: {
data: kms.token.value,
},
};
const resources: Promise<K8sResourceKind>[] = [];

const parsedAddress = parseURL(kms.address.value);

const configData: KMSConfigMap = {
KMS_PROVIDER: 'vault',
VAULT_ADDR: `${`${parsedAddress.protocol}//${parsedAddress.hostname}`}:${kms.port.value}`,
VAULT_BACKEND_PATH: kms.backend,
VAULT_CACERT: kms.caCert?.metadata.name,
VAULT_TLS_SERVER_NAME: kms.tls,
VAULT_CLIENT_CERT: kms.clientCert?.metadata.name,
VAULT_CLIENT_KEY: kms.clientKey?.metadata.name,
VAULT_NAMESPACE: kms.providerNamespace,
};
const configMapObj: ConfigMapKind = {
apiVersion: ConfigMapModel.apiVersion,
kind: ConfigMapModel.kind,
data: {
...configData,
},
metadata: {
name: KMSConfigMapName,
namespace: CEPH_STORAGE_NAMESPACE,
},
};

if (kms.caCert) {
resources.push(k8sCreate(SecretModel, kms.caCert, CEPH_STORAGE_NAMESPACE));
}

if (kms.clientCert) {
resources.push(k8sCreate(SecretModel, kms.clientCert, CEPH_STORAGE_NAMESPACE));
}

if (kms.clientKey) {
resources.push(k8sCreate(SecretModel, kms.clientKey, CEPH_STORAGE_NAMESPACE));
}

resources.push(k8sCreate(SecretModel, tokenSecret, CEPH_STORAGE_NAMESPACE));
resources.push(k8sCreate(ConfigMapModel, configMapObj, CEPH_STORAGE_NAMESPACE));
return resources;
};

export const setEncryptionDispatch = (
keyType: any,
mode: string,
dispatch: React.Dispatch<Action | InternalClusterAction>,
valueType?: any,
) => {
const stateType = mode === MODES.ATTACHED_DEVICES ? _.camelCase(keyType) : keyType;

if (valueType) {
const stateValue =
mode === MODES.ATTACHED_DEVICES ? { value: valueType } : { payload: valueType };
dispatch({ type: stateType, ...stateValue });
} else {
dispatch({ type: stateType });
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.ceph-advanced-kms {
&__form-body {
padding: var(--pf-global--spacer--md) 0 !important;
}
}

0 comments on commit 226c6f3

Please sign in to comment.