Skip to content
This repository was archived by the owner on Apr 28, 2020. It is now read-only.

Commit 4c0463d

Browse files
committed
Add a component for creating baremetal hosts
1 parent 5e0862c commit 4c0463d

File tree

7 files changed

+322
-1
lines changed

7 files changed

+322
-1
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { Modal, Col, Button, Alert } from 'patternfly-react';
4+
5+
import { FormFactory } from '../Form/FormFactory';
6+
import { createBaremetalHost } from '../../k8s/request';
7+
8+
const formFields = {
9+
name: {
10+
id: 'name',
11+
title: 'Name',
12+
required: false,
13+
},
14+
controller: {
15+
id: 'controller',
16+
title: 'Management Controller Address',
17+
required: true,
18+
},
19+
username: {
20+
id: 'username',
21+
title: 'Username',
22+
required: true,
23+
},
24+
password: {
25+
id: 'password',
26+
title: 'Password',
27+
type: 'password',
28+
required: true,
29+
},
30+
};
31+
32+
export class CreateBaremetalHost extends React.Component {
33+
state = {
34+
form: {
35+
value: {},
36+
valid: false,
37+
},
38+
isSubmitting: false,
39+
errorMessage: null,
40+
};
41+
42+
onFormChange = (newValue, target, formValid) => {
43+
this.setState(state => {
44+
const form = { ...state.form };
45+
form.value[target] = {
46+
...newValue,
47+
};
48+
form.valid = formValid;
49+
return { form, errorMessage: null };
50+
});
51+
};
52+
53+
onSubmit = async () => {
54+
this.setState(state => ({
55+
...state,
56+
isSubmitting: true,
57+
}));
58+
const data = this.state.form.value;
59+
const namespace = this.props.selectedNamespace.metadata.name;
60+
const secretName = `${data.name.value}-secret`;
61+
62+
const secret = {
63+
apiVersion: 'v1',
64+
kind: 'Secret',
65+
metadata: {
66+
namespace,
67+
name: secretName,
68+
},
69+
data: {
70+
username: data.username.value,
71+
password: data.password.value,
72+
},
73+
type: 'Opaque',
74+
};
75+
76+
const bmo = {
77+
apiVersion: 'metalkube.org/v1alpha1',
78+
kind: 'BareMetalHost',
79+
metadata: {
80+
name: data.name.value || '',
81+
namespace,
82+
},
83+
spec: {
84+
bmc: {
85+
address: data.controller.value,
86+
credentialsName: secretName,
87+
},
88+
},
89+
};
90+
91+
return createBaremetalHost(this.props.k8sCreate, {
92+
secret,
93+
bmo,
94+
})
95+
.then(result => {
96+
this.setState(state => ({
97+
...state,
98+
isSubmitting: false,
99+
}));
100+
this.props.onClose();
101+
return result;
102+
})
103+
.catch(({ message }) => {
104+
this.setState(state => ({
105+
...state,
106+
isSubmitting: false,
107+
errorMessage: message,
108+
}));
109+
});
110+
};
111+
112+
render() {
113+
return (
114+
<Modal show bsSize="large" onHide={this.props.onClose}>
115+
<Modal.Header closeButton>
116+
<Modal.Title>Add Host</Modal.Title>
117+
</Modal.Header>
118+
<Modal.Body>
119+
<Col sm={12}>
120+
<p style={{ marginTop: '20px' }}>
121+
Specify the host&apos;s name, management controller IP address, and credentials to add it to the cluster.
122+
</p>
123+
<FormFactory
124+
fields={formFields}
125+
fieldsValues={this.state.form.value}
126+
onFormChange={this.onFormChange}
127+
horizontal={false}
128+
/>
129+
</Col>
130+
</Modal.Body>
131+
<Modal.Footer>
132+
{this.state.errorMessage && <Alert type="error">{this.state.errorMessage}</Alert>}
133+
{this.state.isSubmitting && <span className="spinner spinner-xs spinner-inline" />}
134+
<Button bsStyle="default" onClick={this.props.onClose}>
135+
Cancel
136+
</Button>
137+
<Button
138+
bsStyle="primary"
139+
disabled={!this.state.form.valid || this.state.isSubmitting}
140+
onClick={this.onSubmit}
141+
>
142+
Add Host
143+
</Button>
144+
</Modal.Footer>
145+
</Modal>
146+
);
147+
}
148+
}
149+
150+
CreateBaremetalHost.defaultProps = {
151+
selectedNamespace: null,
152+
};
153+
154+
CreateBaremetalHost.propTypes = {
155+
k8sCreate: PropTypes.func.isRequired,
156+
onClose: PropTypes.func.isRequired,
157+
selectedNamespace: PropTypes.object,
158+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { CreateBaremetalHost } from '../CreateBaremetalHost';
2+
3+
export default {
4+
component: CreateBaremetalHost,
5+
props: {
6+
k8sCreate: () => {},
7+
onClose: () => {},
8+
selectedNamespace: {},
9+
},
10+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './CreateBaremetalHost';
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
import { shallow } from 'enzyme';
3+
4+
import CreateBaremetalHostFixture from '../fixtures/CreateBaremetalHost.fixture';
5+
import { CreateBaremetalHost } from '../CreateBaremetalHost';
6+
7+
const testCreateBaremetalHost = () => <CreateBaremetalHost {...CreateBaremetalHostFixture.props} />;
8+
9+
describe('<CreateBaremetalHost />', () => {
10+
it('renders correctly', () => {
11+
const component = shallow(testCreateBaremetalHost());
12+
expect(component).toMatchSnapshot();
13+
});
14+
});
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`<CreateBaremetalHost /> renders correctly 1`] = `
4+
<Modal
5+
animation={true}
6+
autoFocus={true}
7+
backdrop={true}
8+
bsClass="modal"
9+
bsSize="large"
10+
dialogComponentClass={[Function]}
11+
enforceFocus={true}
12+
keyboard={true}
13+
manager={
14+
ModalManager {
15+
"add": [Function],
16+
"containers": Array [],
17+
"data": Array [],
18+
"handleContainerOverflow": true,
19+
"hideSiblingNodes": true,
20+
"isTopModal": [Function],
21+
"modals": Array [],
22+
"remove": [Function],
23+
}
24+
}
25+
onHide={[Function]}
26+
renderBackdrop={[Function]}
27+
restoreFocus={true}
28+
show={true}
29+
>
30+
<ModalHeader
31+
bsClass="modal-header"
32+
closeButton={true}
33+
closeLabel="Close"
34+
>
35+
<ModalTitle
36+
bsClass="modal-title"
37+
componentClass="h4"
38+
>
39+
Add Host
40+
</ModalTitle>
41+
</ModalHeader>
42+
<ModalBody
43+
bsClass="modal-body"
44+
componentClass="div"
45+
>
46+
<Col
47+
bsClass="col"
48+
componentClass="div"
49+
sm={12}
50+
>
51+
<p
52+
style={
53+
Object {
54+
"marginTop": "20px",
55+
}
56+
}
57+
>
58+
Specify the host's name, management controller IP address, and credentials to add it to the cluster.
59+
</p>
60+
<FormFactory
61+
controlSize={5}
62+
fields={
63+
Object {
64+
"controller": Object {
65+
"id": "controller",
66+
"required": true,
67+
"title": "Management Controller Address",
68+
},
69+
"name": Object {
70+
"id": "name",
71+
"required": false,
72+
"title": "Name",
73+
},
74+
"password": Object {
75+
"id": "password",
76+
"required": true,
77+
"title": "Password",
78+
"type": "password",
79+
},
80+
"username": Object {
81+
"id": "username",
82+
"required": true,
83+
"title": "Username",
84+
},
85+
}
86+
}
87+
fieldsValues={Object {}}
88+
horizontal={false}
89+
labelSize={3}
90+
onFormChange={[Function]}
91+
showLabels={true}
92+
textPosition="text-right"
93+
/>
94+
</Col>
95+
</ModalBody>
96+
<ModalFooter
97+
bsClass="modal-footer"
98+
componentClass="div"
99+
>
100+
<Button
101+
active={false}
102+
block={false}
103+
bsClass="btn"
104+
bsStyle="default"
105+
disabled={false}
106+
onClick={[Function]}
107+
>
108+
Cancel
109+
</Button>
110+
<Button
111+
active={false}
112+
block={false}
113+
bsClass="btn"
114+
bsStyle="primary"
115+
disabled={true}
116+
onClick={[Function]}
117+
>
118+
Add Host
119+
</Button>
120+
</ModalFooter>
121+
</Modal>
122+
`;

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export * from './components/Dashboard';
1010
export * from './components/Details';
1111
export * from './components/Dialog';
1212
export * from './components/Form';
13+
export * from './components/CreateBaremetalHost';
1314
// Loading component group not exported
1415
// Table component group not exported
1516
export * from './components/TemplateSource';

src/k8s/request.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,15 @@ import {
1212
getDataVolumeStorageClassName,
1313
getPvcStorageClassName,
1414
} from '../selectors';
15-
import { VirtualMachineModel, ProcessedTemplatesModel, TemplateModel, DataVolumeModel, SecretModel } from '../models';
15+
16+
import {
17+
VirtualMachineModel,
18+
ProcessedTemplatesModel,
19+
TemplateModel,
20+
DataVolumeModel,
21+
SecretModel,
22+
BaremetalHostModel,
23+
} from '../models';
1624

1725
import {
1826
ANNOTATION_DEFAULT_DISK,
@@ -504,3 +512,10 @@ const getDefaultInterface = (vm, template) => {
504512
const defaultInterfaceName = getTemplateAnnotations(template, ANNOTATION_DEFAULT_NETWORK);
505513
return getDevice(vm, 'interfaces', defaultInterfaceName);
506514
};
515+
516+
export const createBaremetalHost = async (k8sCreate, data) => {
517+
const results = [];
518+
results.push(await k8sCreate(SecretModel, data.secret));
519+
results.push(await k8sCreate(BaremetalHostModel, data.bmo));
520+
return results;
521+
};

0 commit comments

Comments
 (0)