Skip to content

Commit

Permalink
ZENKO-2483 create storage location
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas2bert committed Mar 24, 2020
1 parent 3bd516e commit 4bbf698
Show file tree
Hide file tree
Showing 29 changed files with 3,422 additions and 4,451 deletions.
1 change: 1 addition & 0 deletions NOTE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
issue => endpoint "/create" when editing object
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
"dependencies": {
"@fortawesome/fontawesome-free": "5.7.2",
"@scality/core-ui": "github:scality/core-ui.git#add-dist-folder",
"@scality/pensieve-front": "file:../pensieve-front",
"async": "^3.2.0",
"aws-sdk": "^2.616.0",
"connected-react-router": "^6.7.0",
Expand Down
2 changes: 2 additions & 0 deletions src/react/Routes.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Link, Route } from 'react-router-dom';
import DataBrowser from './databrowser/DataBrowser';
import Groups from './group/Groups';
import LocationCreate from './monitor/location/LocationCreate';
import { Navbar } from '@scality/core-ui';
import React from 'react';
import ReplicationCreate from './workflow/replication/ReplicationCreate';
Expand Down Expand Up @@ -72,6 +73,7 @@ class Routes extends React.Component{
<Route path="/databrowser" component={DataBrowser} />
<Route exact path="/workflow" component={Workflows} />
<Route path="/workflow/replication/create" component={ReplicationCreate} />
<Route path="/location/create" component={LocationCreate} />
</Layout>;
}
}
Expand Down
26 changes: 26 additions & 0 deletions src/react/actions/location.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// @noflow
import { handleApiError, handleClientError } from './error';
import { networkEnd, networkStart } from './network';
import { getClients } from '../utils/actions';
import { updateConfiguration } from './configuration';

export function saveLocation(location: Location): ThunkStatePromisedAction {
return (dispatch, getState) => {
const { pensieveClient, instanceId } = getClients(getState());
const params = {
uuid: instanceId,
location,
locationName: location.name,
};

dispatch(networkStart('Saving Location'));
const op = location.objectId ?
pensieveClient.updateConfigurationOverlayLocation(params)
:
pensieveClient.createConfigurationOverlayLocation(params);
return op.then(() => dispatch(updateConfiguration()))
.catch(error => dispatch(handleClientError(error)))
.catch(error => dispatch(handleApiError(error, 'byComponent')))
.finally(() => dispatch(networkEnd()));
};
}
56 changes: 56 additions & 0 deletions src/react/monitor/location/LocationCreate.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Button, Input } from '@scality/core-ui';
import React, { useState } from 'react';
import CreateContainer from '../../ui-elements/CreateContainer';
import { connect } from 'react-redux';
import { newLocationForm } from './utils';
import { saveLocation } from '../../actions';

function LocationCreate(props) {
const [location, setLocation] = useState(newLocationForm());

const onChange = (e) => {
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
const l = {
...location,
[e.target.name]: value,
};
setLocation({ l });
};

const save = (e) => {
if (e) {
e.preventDefault();
}
console.log('location!!!', location);
};

const cancel = () => {
console.log('cannot cancel yet!');
};

return <CreateContainer>
<div className='title'> Add new storage location </div>
<div className='input'>
<div className='name'> location name </div>
<Input
type='text'
name='name'
placeholder='Location Name'
onChange={onChange}
value={location.name}
autoComplete='off' />
</div>
<div className='footer'>
<Button outlined onClick={cancel} text='Cancel'/>
<Button outlined onClick={save} text='Add'/>
</div>
</CreateContainer>;
}

function mapDispatchToProps(dispatch) {
return {
saveLocation: (location: Location) => { dispatch(saveLocation(location)); },
};
}

export default connect(null, mapDispatchToProps)(LocationCreate);
89 changes: 89 additions & 0 deletions src/react/monitor/location/LocationDetails/LocationDetails.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// @flow

import type { EnabledState, InstanceStateSnapshot } from '../../../../types/stats';
import type { LocationDetails as LocationFormDetails, LocationType } from '../../../../types/config';
import React from 'react';
import { Tooltip } from '@scality/core-ui';
// import { TextWithTooltip } from '../../../ui-elements/TooltipComponents';
import { calculateTimeDiffInHours } from '../../../utils';
import { storageOptions } from './storageOptions';

type Props = {
edit: boolean,
locationType: LocationType,
details: LocationFormDetails,
editingExisting: boolean,
repStatus?: EnabledState,
repSchedule?: string,
capabilities?: $PropertyType<InstanceStateSnapshot, 'capabilities'>,
onChange: (v: LocationFormDetails) => void,
};

export default class LocationDetails extends React.Component<Props> {
render() {
if (this.props.edit) {
const loc = storageOptions[this.props.locationType];
if (loc) {
const Details = loc.formDetails;
if (Details) {
return Details &&
<Details
onChange={this.props.onChange}
edit={this.props.edit}
editingExisting={this.props.editingExisting}
details={this.props.details}
locationType={this.props.locationType}
key={this.props.locationType}
capabilities={this.props.capabilities}
/>;
}
}
return null;
}

let msg = 'Replication to this location is paused. All changes queued ' +
'for replication to this location will be processed on resume.';

if (this.props.repSchedule) {
const diff = calculateTimeDiffInHours(this.props.repSchedule);
if (diff && this.props.repSchedule) {
const overlay = new Date(this.props.repSchedule).toString();
msg = (
<div>
<span>Replication to this location is paused. All changes queued for replication to this location will be processed in&nbsp;</span>
<Tooltip overlay={overlay}>
{diff} hours.
</Tooltip>
</div>
);
} else if (this.props.repStatus === 'disabled') {
msg = (
<div>
<span className="mr-2 fa fa-exclamation-circle" />
<span>
Your Zenko instance failed to automatically resume this location. Please resume manually.
</span>
</div>
);
}
}

return (
<div className="p-2">
<div>
Location Type:&nbsp;
<span style={{fontFamily: 'nerissemibold'}} className="px-2">{storageOptions[this.props.locationType].name}</span><br/>
</div>
{
this.props.repStatus === 'disabled' ?
<div id="crr-status-info-text"
className="self-align-center multiple-select-extra-info text-muted"
>
{msg}
</div>
: null
}
</div>
);
}
}
145 changes: 145 additions & 0 deletions src/react/monitor/location/LocationDetails/LocationDetailsAws.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// @flow

import type { LocationDetails } from '../../../../types/config';
import React from 'react';

type Props = {
details: LocationDetails,
onChange: (details: LocationDetails) => void,
editingExisting: boolean,
};

type State = {
serverSideEncryption: boolean,
bucketMatch: boolean,
accessKey: string,
secretKey: string,
bucketName: string,
};

const INIT_STATE: State = {
serverSideEncryption: false,
bucketMatch: false,
accessKey: '',
secretKey: '',
bucketName: '',
};

export default class LocationDetailsAws extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = Object.assign({}, INIT_STATE, this.props.details);
// XXX disable changing it if not provided
this.state.secretKey = '';
}

onChange = (e: SyntheticInputEvent<HTMLInputElement>) => {
const target = e.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
this.setState({ [target.name]: value });
}

updateForm = () => {
if (this.props.onChange) {
this.props.onChange(this.state);
}
}

componentDidMount() {
this.updateForm();
}

shouldComponentUpdate(nextProps: Props, nextState: State) {
return this.state !== nextState;
}

componentDidUpdate() {
this.updateForm();
}

render() {
return (
<div>
<fieldset className="form-group">
<label htmlFor="accessKey">AWS Access Key</label>
<input
name="accessKey"
id="accessKey"
className="form-control"
type="text"
placeholder="AKI5HMPCLRB86WCKTN2C"
value={this.state.accessKey}
onChange={this.onChange}
autoComplete="off"
/>
</fieldset>
<fieldset className="form-group">
<label htmlFor="secretKey">AWS Secret Key</label>
<input
name="secretKey"
id="secretKey"
className="form-control"
type="password"
placeholder="QFvIo6l76oe9xgCAw1N/zlPFtdTSZXMMUuANeXc6"
value={this.state.secretKey}
onChange={this.onChange}
autoComplete="new-password"
/>
<small>
Your credentials are encrypted in transit, then at rest using your
Zenko instance&apos;s RSA key pair so that we&apos;re unable to see them.
</small>
</fieldset>
<fieldset className="form-group">
<label htmlFor="bucketName">Target Bucket Name</label>
<input
name="bucketName"
id="bucketName"
className="form-control"
type="text"
placeholder="Bucket Name"
value={this.state.bucketName}
onChange={this.onChange}
autoComplete="off"
/>
</fieldset>
<fieldset className="form-group">
<label className="form-check-label">
<input
name="bucketMatch"
className="form-check-input"
type="checkbox"
value={this.state.bucketMatch}
checked={this.state.bucketMatch}
disabled={this.props.editingExisting}
onChange={this.onChange} />
<span>Write objects without prefix</span><br />
<small> Use this option for mirroring. <br /> </small>
<small>Store objects in the target bucket without a source-bucket prefix.</small>
{
this.state.bucketMatch &&
<div>
<small className="text-danger">
<i className="fa fa-exclamation-circle"></i>
&nbsp; Storing multiple buckets in a location with this option enabled can lead to data loss.
</small>
</div>
}
</label>
</fieldset>
<fieldset className="form-group">
<label className="form-check-label">
<input
name="serverSideEncryption"
className="form-check-input"
type="checkbox"
value={this.state.serverSideEncryption}
checked={this.state.serverSideEncryption}
onChange={this.onChange} />
<span>Server-Side Encryption</span>
</label>
</fieldset>
</div>
);
}
}
Loading

0 comments on commit 4bbf698

Please sign in to comment.