2018-01-24 04:11:33 +02:00
|
|
|
import React, { PureComponent } from 'react';
|
|
|
|
import intercept from 'apr-intercept';
|
2018-04-12 12:53:00 +03:00
|
|
|
import { Row, Col } from 'joyent-react-styled-flexboxgrid';
|
2018-01-24 04:11:33 +02:00
|
|
|
import { compose, graphql } from 'react-apollo';
|
|
|
|
import { connect } from 'react-redux';
|
|
|
|
import { SubmissionError, destroy } from 'redux-form';
|
|
|
|
import ReduxForm from 'declarative-redux-form';
|
|
|
|
import { set, destroy as destroyValue } from 'react-redux-values';
|
2018-04-12 12:53:00 +03:00
|
|
|
import { Margin, Padding } from 'styled-components-spacing';
|
2018-01-24 04:11:33 +02:00
|
|
|
import find from 'lodash.find';
|
|
|
|
import isBoolean from 'lodash.isboolean';
|
|
|
|
import isArray from 'lodash.isarray';
|
|
|
|
import get from 'lodash.get';
|
|
|
|
|
|
|
|
import {
|
2018-04-12 12:53:00 +03:00
|
|
|
Card,
|
|
|
|
CardOutlet,
|
2018-01-24 04:11:33 +02:00
|
|
|
ViewContainer,
|
|
|
|
StatusLoader,
|
|
|
|
Message,
|
|
|
|
MessageTitle,
|
|
|
|
MessageDescription
|
|
|
|
} from 'joyent-ui-toolkit';
|
|
|
|
|
2018-04-12 12:53:00 +03:00
|
|
|
import {
|
|
|
|
Cns,
|
|
|
|
CnsFooter as Footer,
|
|
|
|
CnsAddServiceForm as AddServiceForm
|
|
|
|
} from 'joyent-ui-resource-widgets';
|
2018-04-06 17:53:44 +03:00
|
|
|
|
|
|
|
import Description from '@components/instances/description';
|
2018-01-24 04:11:33 +02:00
|
|
|
import GetAccount from '@graphql/get-account.gql';
|
2018-02-05 14:54:53 +02:00
|
|
|
import DeleteTag from '@graphql/delete-tag.gql';
|
2018-01-24 04:11:33 +02:00
|
|
|
import UpdateTags from '@graphql/update-tags.gql';
|
|
|
|
import GetTags from '@graphql/list-tags.gql';
|
|
|
|
import parseError from '@state/parse-error';
|
2018-03-01 03:15:16 +02:00
|
|
|
import { addCnsService as validateServiceName } from '@state/validators';
|
2018-01-24 04:11:33 +02:00
|
|
|
|
2018-01-25 00:40:43 +02:00
|
|
|
const FORM_NAME = 'cns-new-service';
|
2018-01-24 04:11:33 +02:00
|
|
|
|
|
|
|
const CnsContainer = ({
|
|
|
|
services = [],
|
|
|
|
hostnames = [],
|
|
|
|
disabled = false,
|
|
|
|
handleToggleCnsEnabled,
|
|
|
|
handleAddService,
|
|
|
|
handleRemoveService,
|
|
|
|
mutating = false,
|
|
|
|
loading = false,
|
|
|
|
mutationError = false,
|
2018-02-23 19:57:13 +02:00
|
|
|
loadingError = null,
|
|
|
|
shouldAsyncValidate,
|
|
|
|
handleAsyncValidate
|
2018-01-24 04:11:33 +02:00
|
|
|
}) => (
|
|
|
|
<ViewContainer main>
|
2018-04-06 17:53:44 +03:00
|
|
|
<Margin bottom="3">
|
2018-04-12 12:53:00 +03:00
|
|
|
<Description href="https://docs.joyent.com/private-cloud/install/cns">
|
|
|
|
Triton CNS is used to automatically update hostnames for your instances.
|
|
|
|
You can serve multiple instances (with multiple IP addresses) under the
|
|
|
|
same hostname by matching the CNS service names.
|
|
|
|
</Description>
|
|
|
|
</Margin>
|
|
|
|
<Row>
|
2018-05-23 19:29:04 +03:00
|
|
|
<Col xs="12" sm="12" md="9">
|
2018-04-12 12:53:00 +03:00
|
|
|
<Card>
|
|
|
|
<CardOutlet>
|
2018-05-23 19:29:04 +03:00
|
|
|
<Padding all="5">
|
2018-04-12 12:53:00 +03:00
|
|
|
{loading ? <StatusLoader /> : null}
|
|
|
|
{!loading && loadingError ? (
|
2018-04-06 17:53:44 +03:00
|
|
|
<Margin bottom="5">
|
2018-04-12 12:53:00 +03:00
|
|
|
<Message error>
|
|
|
|
<MessageTitle>Ooops!</MessageTitle>
|
|
|
|
<MessageDescription>
|
|
|
|
An error occurred while loading your CNS services
|
|
|
|
</MessageDescription>
|
|
|
|
</Message>
|
|
|
|
</Margin>
|
|
|
|
) : null}
|
|
|
|
{!loading && mutationError ? (
|
2018-04-06 17:53:44 +03:00
|
|
|
<Margin bottom="5">
|
2018-04-12 12:53:00 +03:00
|
|
|
<Message error>
|
|
|
|
<MessageTitle>Ooops!</MessageTitle>
|
|
|
|
<MessageDescription>{mutationError}</MessageDescription>
|
|
|
|
</Message>
|
|
|
|
</Margin>
|
|
|
|
) : null}
|
|
|
|
{!loading && !disabled ? (
|
|
|
|
<Cns
|
|
|
|
copy
|
|
|
|
services={services}
|
|
|
|
hostnames={hostnames}
|
|
|
|
onRemoveService={
|
|
|
|
!mutating && (name => handleRemoveService(name, services))
|
|
|
|
}
|
|
|
|
>
|
|
|
|
<ReduxForm
|
|
|
|
form={FORM_NAME}
|
|
|
|
destroyOnUnmount={false}
|
|
|
|
forceUnregisterOnUnmount={true}
|
|
|
|
onSubmit={val => handleAddService(val, services)}
|
|
|
|
shouldAsyncValidate={shouldAsyncValidate}
|
|
|
|
asyncValidate={handleAsyncValidate}
|
|
|
|
>
|
|
|
|
{props => <AddServiceForm {...props} disabled={mutating} />}
|
|
|
|
</ReduxForm>
|
|
|
|
</Cns>
|
|
|
|
) : null}
|
|
|
|
</Padding>
|
|
|
|
</CardOutlet>
|
|
|
|
</Card>
|
|
|
|
{!loading && !loadingError ? (
|
2018-04-06 17:53:44 +03:00
|
|
|
<Margin top="5">
|
2018-04-12 12:53:00 +03:00
|
|
|
<Footer
|
|
|
|
enabled={!disabled}
|
|
|
|
submitting={mutating}
|
|
|
|
onToggle={() => handleToggleCnsEnabled(!disabled)}
|
|
|
|
/>
|
|
|
|
</Margin>
|
|
|
|
) : null}
|
|
|
|
</Col>
|
|
|
|
</Row>
|
2018-01-24 04:11:33 +02:00
|
|
|
</ViewContainer>
|
|
|
|
);
|
|
|
|
|
|
|
|
export { CnsContainer as Cns };
|
|
|
|
|
|
|
|
class CnsClass extends PureComponent {
|
|
|
|
componentWillMount() {
|
|
|
|
const { reset = () => null } = this.props;
|
|
|
|
reset();
|
|
|
|
}
|
2018-02-28 00:40:51 +02:00
|
|
|
|
2018-01-24 04:11:33 +02:00
|
|
|
render() {
|
|
|
|
const { reset, children, ...rest } = this.props;
|
|
|
|
return <CnsContainer {...rest}>{children}</CnsContainer>;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default compose(
|
|
|
|
graphql(UpdateTags, { name: 'updateTags' }),
|
2018-02-05 14:54:53 +02:00
|
|
|
graphql(DeleteTag, { name: 'deleteTag' }),
|
2018-01-24 04:11:33 +02:00
|
|
|
graphql(GetAccount, {
|
2018-02-20 02:35:31 +02:00
|
|
|
options: () => ({
|
|
|
|
ssr: false
|
|
|
|
}),
|
2018-01-24 04:11:33 +02:00
|
|
|
props: ({ data }) => {
|
2018-03-02 00:35:37 +02:00
|
|
|
const id = get(data, 'account.id', '<account-id>');
|
|
|
|
const datacenter = get(data, 'datacenter.name', '<dc>');
|
2018-01-24 04:11:33 +02:00
|
|
|
|
2018-03-02 00:35:37 +02:00
|
|
|
return {
|
|
|
|
id,
|
|
|
|
datacenter
|
|
|
|
};
|
2018-01-24 04:11:33 +02:00
|
|
|
}
|
|
|
|
}),
|
|
|
|
graphql(GetTags, {
|
|
|
|
options: ({ match }) => ({
|
2018-02-26 15:37:37 +02:00
|
|
|
ssr: false,
|
2018-01-24 04:11:33 +02:00
|
|
|
variables: {
|
|
|
|
fetchPolicy: 'network-only',
|
2018-03-21 19:35:51 +02:00
|
|
|
id: get(match, 'params.instance')
|
2018-01-24 04:11:33 +02:00
|
|
|
}
|
|
|
|
}),
|
|
|
|
props: ({ data }) => {
|
2018-03-21 19:35:51 +02:00
|
|
|
const { loading, error, machine, refetch } = data;
|
|
|
|
const tags = get(machine, 'tags', []);
|
2018-01-24 04:11:33 +02:00
|
|
|
|
|
|
|
return {
|
|
|
|
tags,
|
2018-03-21 19:35:51 +02:00
|
|
|
instance: machine,
|
2018-01-24 04:11:33 +02:00
|
|
|
loading,
|
|
|
|
loadingError: error,
|
|
|
|
refetch
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
connect(
|
2018-03-02 00:35:37 +02:00
|
|
|
({ values, form }, { id, datacenter, instance = {}, tags = [] }) => {
|
2018-01-24 04:11:33 +02:00
|
|
|
const { name = '<instance-name>' } = instance;
|
|
|
|
|
|
|
|
const cnsDisable = find(tags, ['name', 'triton.cns.disable']) || {};
|
|
|
|
const cnsServices = find(tags, ['name', 'triton.cns.services']) || {};
|
|
|
|
|
|
|
|
let disabled = JSON.parse(cnsDisable.value || 'false');
|
|
|
|
let services = (cnsServices.value || '').split(/,/gi).filter(Boolean);
|
|
|
|
|
2018-01-25 00:40:43 +02:00
|
|
|
const adding = get(form, `${FORM_NAME}.submitting`, false);
|
2018-01-24 04:11:33 +02:00
|
|
|
const toggling = get(values, `cns-${instance.id}-toggling`, false);
|
|
|
|
const removing = get(values, `cns-${instance.id}-removing`, false);
|
|
|
|
const enabled = get(values, `cns-${instance.id}-enabled`, undefined);
|
2018-02-05 14:54:53 +02:00
|
|
|
const svcs = get(values, `cns-${instance.id}-svcs`, undefined);
|
|
|
|
|
2018-01-24 04:11:33 +02:00
|
|
|
const togglingError = get(
|
|
|
|
values,
|
|
|
|
`cns-${instance.id}-toggling-error`,
|
|
|
|
null
|
|
|
|
);
|
2018-02-05 14:54:53 +02:00
|
|
|
|
2018-01-24 04:11:33 +02:00
|
|
|
const removingError = get(
|
|
|
|
values,
|
|
|
|
`cns-${instance.id}-removing-error`,
|
|
|
|
null
|
|
|
|
);
|
|
|
|
|
|
|
|
if (isBoolean(enabled)) {
|
|
|
|
disabled = !enabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isArray(svcs)) {
|
|
|
|
services = svcs;
|
|
|
|
}
|
|
|
|
|
|
|
|
const defaultHostnames = [
|
|
|
|
{
|
2018-03-02 00:35:37 +02:00
|
|
|
values: [`${name}.inst.${id}.${datacenter}.triton.zone`],
|
2018-01-24 04:11:33 +02:00
|
|
|
public: true
|
|
|
|
},
|
|
|
|
{
|
2018-03-02 00:35:37 +02:00
|
|
|
values: [`${name}.inst.${id}.${datacenter}.cns.joyent.com`]
|
2018-01-24 04:11:33 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
values: [],
|
|
|
|
public: true,
|
|
|
|
service: true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
values: [],
|
|
|
|
service: true
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
const hostnames = defaultHostnames.map(hostname => {
|
|
|
|
if (!hostname.service) {
|
|
|
|
return hostname;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
...hostname,
|
|
|
|
values: services.map(name => {
|
|
|
|
const postfix = hostname.public
|
|
|
|
? '.triton.zone'
|
|
|
|
: '.cns.joyent.com';
|
2018-03-02 00:35:37 +02:00
|
|
|
return `${name}.svc.${id}.${datacenter}${postfix}`;
|
2018-01-24 04:11:33 +02:00
|
|
|
})
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
|
|
|
hostnames,
|
|
|
|
disabled,
|
|
|
|
services,
|
|
|
|
mutating: toggling || removing || adding,
|
|
|
|
mutationError: togglingError || removingError
|
|
|
|
};
|
|
|
|
},
|
2018-02-05 14:54:53 +02:00
|
|
|
(dispatch, { instance = {}, refetch, updateTags, deleteTag }) => ({
|
2018-01-24 04:11:33 +02:00
|
|
|
reset: () => {
|
|
|
|
dispatch([
|
|
|
|
destroyValue({ name: `cns-${instance.id}-removing` }),
|
|
|
|
destroyValue({ name: `cns-${instance.id}-svcs` }),
|
|
|
|
destroyValue({ name: `cns-${instance.id}-removing-error` }),
|
|
|
|
destroyValue({ name: `cns-${instance.id}-toggling` }),
|
|
|
|
destroyValue({ name: `cns-${instance.id}-enabled` }),
|
|
|
|
destroyValue({ name: `cns-${instance.id}-toggling-error` })
|
|
|
|
]);
|
|
|
|
|
|
|
|
return refetch();
|
|
|
|
},
|
2018-03-01 03:15:16 +02:00
|
|
|
shouldAsyncValidate: ({ trigger }) => {
|
|
|
|
return trigger === 'change';
|
2018-02-23 19:57:13 +02:00
|
|
|
},
|
2018-03-01 03:15:16 +02:00
|
|
|
handleAsyncValidate: validateServiceName,
|
2018-01-24 04:11:33 +02:00
|
|
|
handleRemoveService: async (name, services) => {
|
|
|
|
const value = services.filter(svc => name !== svc);
|
|
|
|
|
|
|
|
dispatch([
|
|
|
|
set({ name: `cns-${instance.id}-removing`, value: true }),
|
|
|
|
set({ name: `cns-${instance.id}-svcs`, value })
|
|
|
|
]);
|
|
|
|
|
2018-02-05 14:54:53 +02:00
|
|
|
const newValue = value.join(',');
|
2018-04-06 17:53:44 +03:00
|
|
|
const mutation = newValue.length
|
|
|
|
? updateTags({
|
2018-02-05 14:54:53 +02:00
|
|
|
variables: {
|
|
|
|
id: instance.id,
|
|
|
|
tags: [
|
|
|
|
{
|
|
|
|
name: 'triton.cns.services',
|
|
|
|
value: value.join(',')
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
2018-04-06 17:53:44 +03:00
|
|
|
})
|
|
|
|
: deleteTag({
|
|
|
|
variables: {
|
|
|
|
id: instance.id,
|
|
|
|
name: 'triton.cns.services'
|
|
|
|
}
|
2018-02-05 14:54:53 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
const [err] = await intercept(mutation);
|
2018-01-24 04:11:33 +02:00
|
|
|
|
|
|
|
const setLoadingFalse = set({
|
|
|
|
name: `cns-${instance.id}-removing`,
|
|
|
|
value: false
|
|
|
|
});
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
return dispatch([
|
|
|
|
setLoadingFalse,
|
|
|
|
set({
|
|
|
|
name: `cns-${instance.id}-removing-error`,
|
|
|
|
value: parseError(err)
|
|
|
|
}),
|
|
|
|
set({ name: `cns-${instance.id}-svcs`, value: null })
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return dispatch(setLoadingFalse);
|
|
|
|
},
|
|
|
|
handleToggleCnsEnabled: async disabled => {
|
|
|
|
dispatch([
|
|
|
|
set({ name: `cns-${instance.id}-toggling`, value: true }),
|
|
|
|
set({ name: `cns-${instance.id}-enabled`, value: !disabled })
|
|
|
|
]);
|
|
|
|
|
|
|
|
const [err] = await intercept(
|
|
|
|
updateTags({
|
|
|
|
variables: {
|
|
|
|
id: instance.id,
|
|
|
|
tags: [
|
|
|
|
{
|
|
|
|
name: 'triton.cns.disable',
|
|
|
|
value: disabled ? 'true' : 'false'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
const setLoadingFalse = set({
|
|
|
|
name: `cns-${instance.id}-toggling`,
|
|
|
|
value: false
|
|
|
|
});
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
return dispatch([
|
|
|
|
setLoadingFalse,
|
|
|
|
set({
|
|
|
|
name: `cns-${instance.id}-toggling-error`,
|
|
|
|
value: parseError(err)
|
|
|
|
}),
|
|
|
|
set({ name: `cns-${instance.id}-enabled`, value: null })
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return dispatch(setLoadingFalse);
|
|
|
|
},
|
|
|
|
handleAddService: async ({ name }, services) => {
|
|
|
|
const value = services.concat(name);
|
|
|
|
|
|
|
|
dispatch(set({ name: `cns-${instance.id}-svcs`, value }));
|
|
|
|
|
|
|
|
const [err] = await intercept(
|
|
|
|
updateTags({
|
|
|
|
variables: {
|
|
|
|
id: instance.id,
|
|
|
|
tags: [
|
|
|
|
{
|
|
|
|
name: 'triton.cns.services',
|
|
|
|
value: value.join(',')
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
dispatch(set({ name: `cns-${instance.id}-svcs`, services }));
|
|
|
|
|
|
|
|
throw new SubmissionError({
|
|
|
|
_error: parseError(err)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-01-25 00:40:43 +02:00
|
|
|
return dispatch(destroy(FORM_NAME));
|
2018-01-24 04:11:33 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
)
|
|
|
|
)(CnsClass);
|