2018-01-24 04:11:33 +02:00
|
|
|
import React, { PureComponent } from 'react';
|
|
|
|
import intercept from 'apr-intercept';
|
|
|
|
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';
|
|
|
|
import { Margin } from 'styled-components-spacing';
|
|
|
|
import find from 'lodash.find';
|
|
|
|
import isBoolean from 'lodash.isboolean';
|
|
|
|
import isArray from 'lodash.isarray';
|
|
|
|
import get from 'lodash.get';
|
|
|
|
|
|
|
|
import {
|
|
|
|
ViewContainer,
|
|
|
|
StatusLoader,
|
|
|
|
Message,
|
|
|
|
MessageTitle,
|
|
|
|
MessageDescription
|
|
|
|
} from 'joyent-ui-toolkit';
|
|
|
|
|
|
|
|
import Description from '@components/description';
|
|
|
|
import Cns, { Footer, AddServiceForm } from '@components/cns';
|
|
|
|
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-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,
|
|
|
|
loadingError = null
|
|
|
|
}) => (
|
|
|
|
<ViewContainer main>
|
|
|
|
<Margin bottom={1}>
|
|
|
|
<Description>
|
|
|
|
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.{' '}
|
|
|
|
<a
|
|
|
|
href="https://docs.joyent.com/private-cloud/install/cns"
|
|
|
|
target="_blank"
|
|
|
|
rel="noopener noreferrer"
|
|
|
|
>
|
|
|
|
Read the docs
|
|
|
|
</a>
|
|
|
|
</Description>
|
|
|
|
</Margin>
|
|
|
|
{loading ? <StatusLoader /> : null}
|
|
|
|
{!loading && loadingError ? (
|
2018-01-30 18:04:03 +02:00
|
|
|
<Margin bottom={4}>
|
|
|
|
<Message error>
|
|
|
|
<MessageTitle>Ooops!</MessageTitle>
|
|
|
|
<MessageDescription>
|
|
|
|
An error occurred while loading your CNS services
|
|
|
|
</MessageDescription>
|
|
|
|
</Message>
|
|
|
|
</Margin>
|
2018-01-24 04:11:33 +02:00
|
|
|
) : null}
|
|
|
|
{!loading && mutationError ? (
|
2018-01-30 18:04:03 +02:00
|
|
|
<Margin bottom={4}>
|
|
|
|
<Message error>
|
|
|
|
<MessageTitle>Ooops!</MessageTitle>
|
|
|
|
<MessageDescription>{mutationError}</MessageDescription>
|
|
|
|
</Message>
|
|
|
|
</Margin>
|
2018-01-24 04:11:33 +02:00
|
|
|
) : null}
|
|
|
|
{!loading && !disabled ? (
|
2018-02-01 17:33:58 +02:00
|
|
|
<Margin bottom={4}>
|
|
|
|
<Cns
|
|
|
|
copy
|
|
|
|
services={services}
|
|
|
|
hostnames={hostnames}
|
|
|
|
onRemoveService={
|
|
|
|
!mutating && (name => handleRemoveService(name, services))
|
|
|
|
}
|
2018-01-24 04:11:33 +02:00
|
|
|
>
|
2018-02-01 17:33:58 +02:00
|
|
|
<ReduxForm
|
|
|
|
form={FORM_NAME}
|
|
|
|
destroyOnUnmount={false}
|
|
|
|
forceUnregisterOnUnmount={true}
|
|
|
|
onSubmit={val => handleAddService(val, services)}
|
|
|
|
>
|
|
|
|
{props => <AddServiceForm {...props} disabled={mutating} />}
|
|
|
|
</ReduxForm>
|
|
|
|
</Cns>
|
|
|
|
</Margin>
|
2018-01-24 04:11:33 +02:00
|
|
|
) : null}
|
|
|
|
{!loading && !loadingError ? (
|
|
|
|
<Footer
|
|
|
|
enabled={!disabled}
|
|
|
|
submitting={mutating}
|
|
|
|
onToggle={() => handleToggleCnsEnabled(!disabled)}
|
|
|
|
/>
|
|
|
|
) : null}
|
|
|
|
</ViewContainer>
|
|
|
|
);
|
|
|
|
|
|
|
|
export { CnsContainer as Cns };
|
|
|
|
|
|
|
|
class CnsClass extends PureComponent {
|
|
|
|
componentWillMount() {
|
|
|
|
const { reset = () => null } = this.props;
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
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, {
|
|
|
|
props: ({ data }) => {
|
|
|
|
const { account = {} } = data;
|
|
|
|
const { id = '<account-id>' } = account;
|
|
|
|
|
|
|
|
return { id };
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
graphql(GetTags, {
|
|
|
|
options: ({ match }) => ({
|
|
|
|
variables: {
|
|
|
|
fetchPolicy: 'network-only',
|
|
|
|
name: get(match, 'params.instance')
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
props: ({ data }) => {
|
|
|
|
const { loading, error, variables, refetch, ...rest } = data;
|
|
|
|
const { name } = variables;
|
|
|
|
|
|
|
|
const instance = find(get(rest, 'machines', []), ['name', name]);
|
|
|
|
const tags = get(instance, 'tags', []);
|
|
|
|
|
|
|
|
return {
|
|
|
|
tags,
|
|
|
|
instance,
|
|
|
|
loading,
|
|
|
|
loadingError: error,
|
|
|
|
refetch
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
connect(
|
|
|
|
({ values, form }, { id, instance = {}, tags = [] }) => {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// REPLACE WITH DATA CENTER
|
|
|
|
const dataCenter = 'us-east-1';
|
|
|
|
|
|
|
|
const defaultHostnames = [
|
|
|
|
{
|
|
|
|
values: [`${name}.inst.${id}.${dataCenter}.triton.zone`],
|
|
|
|
public: true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
values: [`${name}.inst.${id}.${dataCenter}.cns.joyent.com`]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
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';
|
|
|
|
return `${name}.svc.${id}.${dataCenter}${postfix}`;
|
|
|
|
})
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
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();
|
|
|
|
},
|
|
|
|
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(',');
|
|
|
|
const mutation = !newValue.length
|
|
|
|
? deleteTag({
|
|
|
|
variables: {
|
|
|
|
id: instance.id,
|
|
|
|
name: 'triton.cns.services'
|
|
|
|
}
|
|
|
|
})
|
|
|
|
: updateTags({
|
|
|
|
variables: {
|
|
|
|
id: instance.id,
|
|
|
|
tags: [
|
|
|
|
{
|
|
|
|
name: 'triton.cns.services',
|
|
|
|
value: value.join(',')
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
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);
|