feat: environment stage

This commit is contained in:
Sérgio Ramos 2017-07-05 14:33:16 +01:00 committed by Judit Greskovits
parent 9730b5733a
commit 5ccd873a54
13 changed files with 353 additions and 138 deletions

View File

@ -41,6 +41,7 @@
"react-redux": "^5.0.5",
"react-router": "^4.1.1",
"react-router-dom": "^4.1.1",
"react-simple-table": "^1.0.1",
"react-styled-flexboxgrid": "^2.0.1",
"redux": "^3.6.0",
"redux-actions": "^2.0.3",

View File

@ -1,6 +1,7 @@
import React from 'react';
import { Field } from 'redux-form';
import styled from 'styled-components';
import SimpleTable from 'react-simple-table';
import { Row, Col } from 'react-styled-flexboxgrid';
import { Dots2 } from 'styled-text-spinners';
import Bundle from 'react-bundle';
@ -48,9 +49,12 @@ const ButtonsRow = Row.extend`
margin-bottom: ${remcalc(60)};
`;
const Editor = ManifestEditor => ({ input, defaultValue }) =>
const MEditor = ManifestEditor => ({ input, defaultValue }) =>
<ManifestEditor mode="yaml" {...input} value={input.value || defaultValue} />;
const EEditor = ManifestEditor => ({ input, defaultValue }) =>
<ManifestEditor mode="ini" {...input} value={input.value || defaultValue} />;
export const Name = ({ handleSubmit, onCancel, dirty }) =>
<form onSubmit={handleSubmit}>
<Row>
@ -67,14 +71,7 @@ export const Name = ({ handleSubmit, onCancel, dirty }) =>
</ButtonsRow>
</form>;
export const Manifest = ({
handleSubmit,
onCancel,
dirty,
defaultValue,
mode,
loading
}) =>
export const Manifest = ({ handleSubmit, onCancel, dirty, defaultValue }) =>
<form onSubmit={handleSubmit}>
<Bundle load={() => import('joyent-manifest-editor')}>
{ManifestEditor =>
@ -82,7 +79,33 @@ export const Manifest = ({
? <Field
name="manifest"
defaultValue={defaultValue}
component={Editor(ManifestEditor)}
component={MEditor(ManifestEditor)}
/>
: <Dots2 />}
</Bundle>
<ButtonsRow>
<Button onClick={onCancel} secondary>Cancel</Button>
<Button disabled={!dirty} type="submit">
Environment
</Button>
</ButtonsRow>
</form>;
export const Environment = ({
handleSubmit,
onCancel,
dirty,
defaultValue,
loading
}) =>
<form onSubmit={handleSubmit}>
<Bundle load={() => import('joyent-manifest-editor')}>
{ManifestEditor =>
ManifestEditor
? <Field
name="environment"
defaultValue={defaultValue}
component={EEditor(ManifestEditor)}
/>
: <Dots2 />}
</Bundle>
@ -95,11 +118,29 @@ export const Manifest = ({
</form>;
export const Review = ({ handleSubmit, onCancel, dirty, ...state }) => {
const serviceList = forceArray(state.services).map(({ name, image }) =>
const serviceList = forceArray(state.services).map(({ name, config }) =>
<ServiceCard key={name}>
<Dl>
<dt><ServiceName>{name}</ServiceName></dt>
<dt><ImageTitle>Image:</ImageTitle> <Image>{image}</Image></dt>
<dt><ImageTitle>Image:</ImageTitle> <Image>{config.image}</Image></dt>
{config.environment.length
? <dt><ImageTitle>Environment:</ImageTitle></dt>
: undefined}
{config.environment.length
? <SimpleTable
columns={[
{
columnHeader: 'Name',
path: 'name'
},
{
columnHeader: 'Value',
path: 'value'
}
]}
data={config.environment}
/>
: undefined}
</Dl>
</ServiceCard>
);
@ -136,7 +177,7 @@ export const Progress = ({ stage, create, edit }) => {
</ProgressbarButton>
</ProgressbarItem>;
const _manifestCompleted = stage === 'review';
const _manifestCompleted = ['environment', 'review'].indexOf(stage) >= 0;
const _manifestActive = create ? stage === 'manifest' : stage === 'edit';
const _manifest = (
@ -147,7 +188,22 @@ export const Progress = ({ stage, create, edit }) => {
active={_manifestActive}
first={edit}
>
Define services
Define Services
</ProgressbarButton>
</ProgressbarItem>
);
const _environmentCompleted = stage === 'review';
const _environmentActive = stage === 'environment';
const _environment = (
<ProgressbarItem>
<ProgressbarButton
zIndex="8"
completed={_environmentCompleted}
active={_environmentActive}
>
Define Environment
</ProgressbarButton>
</ProgressbarItem>
);
@ -156,7 +212,7 @@ export const Progress = ({ stage, create, edit }) => {
const _review = (
<ProgressbarItem>
<ProgressbarButton zIndex="8" active={stage === 'review'} last>
<ProgressbarButton zIndex="7" active={_reviewActive} last>
Review and deploy
</ProgressbarButton>
</ProgressbarItem>
@ -166,6 +222,7 @@ export const Progress = ({ stage, create, edit }) => {
<Progressbar>
{_name}
{_manifest}
{_environment}
{_review}
</Progressbar>
);

View File

@ -96,6 +96,7 @@ const ServiceListItem = ({
</CardDescription>
<CardOptions onClick={handleCardOptionsClick} />
</StyledCardHeader>;
const view = children
? <CardGroupView>
{children}

View File

@ -12,7 +12,12 @@ import DeploymentGroupProvisionMutation from '@graphql/DeploymentGroupProvision.
import DeploymentGroupConfigQuery from '@graphql/DeploymentGroupConfig.gql';
import { client } from '@state/store';
import { Name, Manifest, Review } from '@components/deployment-groups/create';
import {
Name,
Manifest,
Environment,
Review
} from '@components/deployment-groups/create';
// TODO: move state to redux. why: because in redux we can cache transactional
// state between refreshes
@ -51,6 +56,12 @@ class DeploymentGroupEditOrCreate extends Component {
forceUnregisterOnUnmount: true
})(Manifest);
const EnvironmentForm = reduxForm({
form: `${type}-deployment-group`,
destroyOnUnmount: true,
forceUnregisterOnUnmount: true
})(Environment);
const ReviewForm = reduxForm({
form: `${type}-deployment-group`,
destroyOnUnmount: true,
@ -62,17 +73,20 @@ class DeploymentGroupEditOrCreate extends Component {
manifestStage: create ? 'manifest' : 'edit',
name: '',
manifest: '',
environment: '',
services: [],
loading: false,
error: null,
NameForm,
ManifestForm,
EnvironmentForm,
ReviewForm
};
this.stages = {
name: create && this.renderNameForm.bind(this),
[create ? 'manifest' : 'edit']: this.renderManifestEditor.bind(this),
environment: this.renderEnvironmentEditor.bind(this),
review: this.renderReview.bind(this)
};
@ -80,6 +94,7 @@ class DeploymentGroupEditOrCreate extends Component {
type === 'create' && this.handleNameSubmit.bind(this);
this.handleManifestSubmit = this.handleManifestSubmit.bind(this);
this.handleEnvironmentSubmit = this.handleEnvironmentSubmit.bind(this);
this.handleReviewSubmit = this.handleReviewSubmit.bind(this);
this.handleCancel = this.handleCancel.bind(this);
@ -117,7 +132,7 @@ class DeploymentGroupEditOrCreate extends Component {
};
provision = async deploymentGroupId => {
const { manifest } = this.state;
const { manifest, environment } = this.state;
const { provisionManifest } = this.props;
const [err] = await intercept(
@ -125,6 +140,7 @@ class DeploymentGroupEditOrCreate extends Component {
deploymentGroupId,
type: 'COMPOSE',
format: 'YAML',
environment,
raw: manifest
})
);
@ -145,16 +161,24 @@ class DeploymentGroupEditOrCreate extends Component {
}
handleManifestSubmit({ manifest = '' }) {
const { name } = this.state;
this.setState({ manifest }, () => {
this.redirect({ stage: 'environment', prog: true });
});
}
handleEnvironmentSubmit({ environment = '' }) {
const { name, manifest } = this.state;
const getConfig = async () => {
const [err, conf] = await intercept(
client.query({
query: DeploymentGroupConfigQuery,
fetchPolicy: 'network-only',
variables: {
deploymentGroupName: name,
type: 'COMPOSE',
format: 'YAML',
environment,
raw: manifest
}
})
@ -174,7 +198,7 @@ class DeploymentGroupEditOrCreate extends Component {
});
};
this.setState({ manifest, loading: true }, getConfig);
this.setState({ environment, loading: true }, getConfig);
}
handleReviewSubmit() {
@ -237,6 +261,18 @@ class DeploymentGroupEditOrCreate extends Component {
defaultValue={this.props.manifest}
onSubmit={this.handleManifestSubmit}
onCancel={this.handleCancel}
/>
);
}
renderEnvironmentEditor() {
const { EnvironmentForm } = this.state;
return (
<EnvironmentForm
defaultValue={this.props.environment}
onSubmit={this.handleEnvironmentSubmit}
onCancel={this.handleCancel}
loading={this.state.loading}
/>
);
@ -276,10 +312,20 @@ class DeploymentGroupEditOrCreate extends Component {
return this.redirect({ stage: defaultStage });
}
if (stage === 'review' && !this.state.manifest) {
if (stage === 'environment' && !this.state.manifest) {
return this.redirect({ stage: manifestStage });
}
if (stage === 'review' && !this.state.environment) {
if (!this.state.manifest) {
return this.redirect({ stage: manifestStage });
}
if (!this.state.environment) {
return this.redirect({ stage: 'environment' });
}
}
return this.stages[stage]();
}
}

View File

@ -1,8 +1,22 @@
#import "./ServiceInfo.gql"
query config($deploymentGroupName: String!, $type: ManifestType!, $format: ManifestFormat!, $raw: String!) {
config(deploymentGroupName: $deploymentGroupName, type: $type, format: $format, raw: $raw) {
image
query config($deploymentGroupName: String!, $type: ManifestType!, $format: ManifestFormat!, $environment: String!, $raw: String!) {
config(deploymentGroupName: $deploymentGroupName, type: $type, format: $format, environment: $environment, raw: $raw) {
...ServiceInfo
config {
id
image
ports
environment {
id
name
value
}
labels {
id
name
value
}
}
}
}

View File

@ -1,5 +1,5 @@
mutation provisionManifest($deploymentGroupId: ID!, $type: ManifestType!, $format: ManifestFormat!, $raw: String!) {
provisionManifest(deploymentGroupId: $deploymentGroupId, type: $type, format: $format, raw: $raw) {
mutation provisionManifest($deploymentGroupId: ID!, $type: ManifestType!, $format: ManifestFormat!, $environment: String!, $raw: String!) {
provisionManifest(deploymentGroupId: $deploymentGroupId, type: $type, format: $format, environment: $environment, raw: $raw) {
scale {
serviceName
replicas

View File

@ -68,15 +68,14 @@ const activeInstanceStatuses = [
'INCOMPLETE'
];
const getInstanceStatuses = (service) => {
const getInstanceStatuses = service => {
const instanceStatuses = service.instances.reduce((statuses, instance) => {
// if (instance.status !== 'RUNNING') {
if (statuses[instance.status]) {
statuses[instance.status]++;
} else {
statuses[instance.status] = 1;
}
if (statuses[instance.status]) {
statuses[instance.status]++;
} else {
statuses[instance.status] = 1;
}
// }
return statuses;
}, {});
@ -85,31 +84,35 @@ const getInstanceStatuses = (service) => {
status,
count: instanceStatuses[status]
}));
}
const getInstancesActive = (instanceStatuses) => {
return instanceStatuses.reduce((active, instanceStatus) =>
activeInstanceStatuses.indexOf(instanceStatus.status) === -1 ?
active : true, false);
}
const getService = (service, index) => {
const statuses = getInstanceStatuses(service);
const instancesActive = getInstancesActive(statuses);
const instanceStatuses = statuses.length === 1 && statuses[0].status === 'RUNNING' ?
[] : statuses;
return ({
index,
...service,
instanceStatuses,
instancesActive,
isConsul: service.slug === 'consul'
});
};
const processServices = (services) => {
const getInstancesActive = instanceStatuses => {
return instanceStatuses.reduce(
(active, instanceStatus) =>
activeInstanceStatuses.indexOf(instanceStatus.status) === -1
? active
: true,
false
);
};
const getService = (service, index) => {
const statuses = getInstanceStatuses(service);
const instancesActive = getInstancesActive(statuses);
const instanceStatuses = statuses.length === 1 &&
statuses[0].status === 'RUNNING'
? []
: statuses;
return {
index,
...service,
instanceStatuses,
instancesActive,
isConsul: service.slug === 'consul'
};
};
const processServices = services => {
return forceArray(services).reduce((ss, s, i) => {
if (s.parent) {
const parents = ss.filter(parentS => parentS.id === s.parent);
@ -124,7 +127,9 @@ const processServices = (services) => {
parent.children = [];
}
const child = getService(s, i);
parent.instancesActive = parent.instancesActive ? true : child.instancesActive;
parent.instancesActive = parent.instancesActive
? true
: child.instancesActive;
parent.children.push(child);
} else {
const serviceIndex = ss.findIndex(existingS => existingS.id === s.id);
@ -141,19 +146,22 @@ const processServices = (services) => {
}, []);
};
const processServicesForTopology = (services) => {
const processServicesForTopology = services => {
const processedServices = processServices(services);
const connectedServices = processedServices.reduce((connections, service) =>
service.connections && service.connections.length ?
connections.concat(service.connections).concat(service.id) : connections, []);
const connectedServices = processedServices.reduce(
(connections, service) =>
service.connections && service.connections.length
? connections.concat(service.connections).concat(service.id)
: connections,
[]
);
return processedServices.map(service => ({
...service,
connected: connectedServices.indexOf(service.id) !== -1
}));
}
...service,
connected: connectedServices.indexOf(service.id) !== -1
}));
};
export {
deploymentGroupBySlug as deploymentGroupBySlugSelector,

View File

@ -1,6 +1,3 @@
scalar Date
scalar Object
type Portal {
id: ID!
user: User!
@ -36,6 +33,7 @@ type DeploymentGroup {
}
type ServiceScale {
id: ID!
serviceName: String!
replicas: Int!
}
@ -50,6 +48,7 @@ enum ConvergenceActionType {
}
type ConvergenceAction {
id: ID!
type: ConvergenceActionType!
service: String! # service name
toProcess: Int, # merely used for book keeping
@ -58,6 +57,7 @@ type ConvergenceAction {
}
type Version {
id: ID!
manifest: Manifest!
scale(serviceName: String): [ServiceScale]!
plan: [ConvergenceAction]
@ -79,8 +79,8 @@ type Manifest {
id: ID!
type: ManifestType!
format: ManifestFormat!
environment: String!
raw: String!
obj: Object
}
enum ServiceStatus {
@ -95,6 +95,21 @@ enum ServiceStatus {
UNKNOWN
}
type KeyValue {
id: ID!
name: String!
value: String!
}
type ServiceConfig {
id: ID!
package: Package # we don't have this in current mock data
environment: [KeyValue]
image: String # used only for config
labels: [KeyValue]
ports: [String]
}
# immutable
type Service {
id: ID! # unique id for db row
@ -102,11 +117,9 @@ type Service {
name: String! # human readable name
slug: String!
instances(name: String, machineId: ID, status: InstanceStatus): [Instance]!
connections: [String!] # list of serviceIds
connections: [String] # list of serviceIds
parent: ID # parent service id
package: Package! # we don't have this in current mock data,
environment: [Environment]
image: String # used only for config
config: ServiceConfig
status: ServiceStatus
}
@ -124,12 +137,6 @@ type Package {
group: String!
}
# environment variables
type Environment {
name: String!
value: String!
}
enum InstanceStatus {
PROVISIONING
READY
@ -184,7 +191,7 @@ type Query {
datacenter(id: ID, region: String): Datacenter
datacenters: [Datacenter]
config(deploymentGroupName: String!, type: ManifestType!, format: ManifestFormat!, raw: String!): [Service]
config(deploymentGroupName: String!, type: ManifestType!, format: ManifestFormat!, environment: String!, raw: String!): [Service]
importableDeploymentGroups: [DeploymentGroup]
}
@ -192,7 +199,7 @@ type Mutation {
createDeploymentGroup(name: String!): DeploymentGroup
updateDeploymentGroup(id: ID!, name: String!): DeploymentGroup
provisionManifest(deploymentGroupId: ID!, type: ManifestType!, format: ManifestFormat!, raw: String!): Version
provisionManifest(deploymentGroupId: ID!, type: ManifestType!, format: ManifestFormat!, environment: String!, raw: String!): Version
scale(serviceId: ID!, replicas: Int!): Version
stopServices(ids: [ID]!): [Service]

View File

@ -22,13 +22,17 @@ module.exports = class DockerComposeClient extends EventEmitter {
return this.client.close();
}
provision({ projectName, manifest }, cb) {
// eslint-disable-next-line camelcase
return this._invoke('up', { project_name: projectName }, manifest, cb);
provision({ projectName, environment, manifest }, cb) {
return this._invoke('up', {
// eslint-disable-next-line camelcase
project_name: projectName,
environment
}, manifest, cb);
}
scale({ projectName, services, manifest }, cb) {
scale({ projectName, services, environment, manifest }, cb) {
const options = {
environment,
// eslint-disable-next-line camelcase
project_name: projectName,
services: Object.keys(services).map(name => ({
@ -40,10 +44,11 @@ module.exports = class DockerComposeClient extends EventEmitter {
return this._invoke('scale', options, manifest, cb);
}
config({ projectName, manifest }, cb) {
config({ projectName, environment, manifest }, cb) {
const options = {
// eslint-disable-next-line camelcase
project_name: projectName
project_name: projectName,
environment
};
return this._invoke('config', options, manifest, cb);

View File

@ -4,6 +4,7 @@ import 'codemirror/addon/fold/foldgutter.css';
import 'codemirror/addon/lint/lint.css';
import 'codemirror/mode/yaml/yaml';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/mode/properties/properties';
import 'codemirror/addon/edit/closebrackets';
import 'codemirror/addon/edit/matchbrackets';
import 'codemirror/addon/fold/foldcode';
@ -60,7 +61,8 @@ class ManifestEditor extends Component {
name: 'javascript',
json: true
},
yaml: 'yaml'
yaml: 'yaml',
ini: 'properties'
};
return Object.assign({}, options, {

View File

@ -13,7 +13,6 @@ const Hoek = require('hoek');
const Triton = require('triton');
const ParamCase = require('param-case');
const Penseur = require('penseur');
const { DEPLOYMENT_GROUP, SERVICE, HASH } = require('../watch');
const UniqBy = require('lodash.uniqby');
const Find = require('lodash.find');
const Get = require('lodash.get');
@ -24,6 +23,7 @@ const VAsync = require('vasync');
// local modules
const Transform = require('./transform');
const { DEPLOYMENT_GROUP, SERVICE, HASH } = require('../watch');
const NON_IMPORTABLE_STATES = [
@ -153,7 +153,11 @@ class Data extends EventEmitter {
const portal = portals.shift();
// Sub query/filter for deploymentGroups
const deploymentGroups = (args) => {
const deploymentGroups = (args, cb) => {
if (typeof cb === 'function') {
return this.getDeploymentGroups(args, cb);
}
return new Promise((resolve, reject) => {
this.getDeploymentGroups(args, internals.resolveCb(resolve, reject));
});
@ -372,7 +376,7 @@ class Data extends EventEmitter {
}
if (!deploymentGroups || !deploymentGroups.length) {
return cb(null, {});
return cb();
}
cb(null, Transform.fromDeploymentGroup(this._getDeploymentGroupFns(deploymentGroups[0])));
@ -512,6 +516,7 @@ class Data extends EventEmitter {
]);
return {
id: Uuid(),
serviceName: name,
replicas: Number.isFinite(currentScale) ? currentScale : 1
};
@ -554,6 +559,7 @@ class Data extends EventEmitter {
});
return {
id: Uuid(),
serviceName: name,
replicas: existingMachines.length ? existingMachines.length : 1
};
@ -565,7 +571,7 @@ class Data extends EventEmitter {
this._listMachines(deploymentGroupName, handleMachinesList);
}
scale ({ serviceId, replicas }, cb) {
scale ({ serviceId, environment, replicas }, cb) {
Hoek.assert(serviceId, 'service id is required');
Hoek.assert(typeof replicas === 'number' && replicas >= 0, 'replicas must be a number no less than 0');
@ -636,6 +642,7 @@ class Data extends EventEmitter {
this._dockerCompose.scale({
projectName: ctx.deploymentGroup.name,
environment,
services: {
[ctx.service.name]: replicas
},
@ -647,6 +654,7 @@ class Data extends EventEmitter {
const getNewScale = () => {
return ctx.currentScale.map(({ serviceName, replicas }) => {
return {
id: Uuid(),
serviceName: serviceName,
replicas: serviceName === ctx.service.name ?
(ctx.serviceScale + ctx.diff) :
@ -661,6 +669,7 @@ class Data extends EventEmitter {
deploymentGroupId: ctx.deploymentGroup.id,
scale: getNewScale(),
plan: [{
id: Uuid(),
type: 'REMOVE',
service: ctx.service.name,
toProcess: Math.abs(ctx.diff),
@ -683,6 +692,7 @@ class Data extends EventEmitter {
deploymentGroupId: ctx.deploymentGroup.id,
scale: getNewScale(),
plan: [{
id: Uuid(),
type: 'CREATE',
service: ctx.service.name,
toProcess: Math.abs(ctx.diff),
@ -908,6 +918,7 @@ class Data extends EventEmitter {
if (!provision) {
return {
id: Uuid(),
type: 'REMOVE',
service: name,
toProcess: machines.length,
@ -918,6 +929,7 @@ class Data extends EventEmitter {
const ActionMap = {
'NOOP': () => {
return {
id: Uuid(),
type: 'NOOP',
service: name,
machines
@ -925,6 +937,7 @@ class Data extends EventEmitter {
},
'CREATE': () => {
return {
id: Uuid(),
type: 'CREATE',
service: name,
toProcess: scale,
@ -933,6 +946,7 @@ class Data extends EventEmitter {
},
'RECREATE': () => {
return {
id: Uuid(),
type: 'CREATE',
service: name,
toProcess: machines.length,
@ -941,6 +955,7 @@ class Data extends EventEmitter {
},
'START': () => {
return {
id: Uuid(),
type: 'START',
service: name,
machines
@ -1077,12 +1092,17 @@ class Data extends EventEmitter {
const action = Get(provision, 'plan.action', 'noop').toUpperCase();
const service = services.shift();
const { config } = Find(ctx.config, ['name', serviceName], {
config: {}
});
const payload = {
hash: provision.hash,
deploymentGroupId: ctx.currentDeploymentGroup.id,
name: serviceName,
slug: ParamCase(serviceName),
status: ServiceStatusFromPlan[action]
status: ServiceStatusFromPlan[action],
config
};
return !service ?
@ -1133,6 +1153,7 @@ class Data extends EventEmitter {
this._dockerCompose.provision({
projectName: ctx.currentDeploymentGroup.name,
environment: clientManifest.environment,
manifest: ctx.newManifest.raw
}, handleProvisionResponse);
});
@ -1830,23 +1851,19 @@ class Data extends EventEmitter {
});
}
updateInstance ({ id, status, healthy}, cb) {
const changes = { id };
updateInstance (clientInstance, cb) {
const instance = Transform.toInstance(clientInstance);
if (typeof healthy === 'boolean') {
changes.healthy = healthy;
if (typeof instance.healthy !== 'boolean') {
instance.healthy = null;
}
if (status) {
changes.status = status;
}
this._db.instances.update([changes], (err) => {
this._db.instances.update([instance], (err) => {
if (err) {
return cb(err);
}
this.getInstance({ id }, cb);
this.getInstance({ id: instance.id }, cb);
});
}
@ -1989,7 +2006,7 @@ class Data extends EventEmitter {
});
}
getConfig ({deploymentGroupName = '', type = '', format = '', raw = '' }, cb) {
getConfig ({deploymentGroupName = '', type = '', format = '', environment = '', raw = '' }, cb) {
if (type.toUpperCase() !== 'COMPOSE') {
return cb(new Error('"COMPOSE" is the only `type` supported'));
}
@ -2002,6 +2019,7 @@ class Data extends EventEmitter {
this._dockerCompose.config({
projectName: deploymentGroupName,
environment,
manifest: raw
}, (err, config = {}) => {
if (err) {
@ -2021,15 +2039,34 @@ class Data extends EventEmitter {
}
cb(null, Object.keys(services).reduce((acc, serviceName) => {
const environment = Get(services, `${serviceName}.environment`, {});
const labels = Get(services, `${serviceName}.labels`, {});
const ports = Get(services, `${serviceName}.ports`, []);
const image = Get(services, `${serviceName}.image`, '');
const toKeyValue = (v) => {
return Object.keys(v).map((key) => {
return {
id: Uuid(),
name: key,
value: v[key]
};
});
};
return acc.concat([{
id: Uuid(),
hash: Uuid(),
name: serviceName,
slug: ParamCase(serviceName),
instances: [],
package: {},
active: true,
image: services[serviceName].image
config: {
id: Uuid(),
environment: toKeyValue(environment),
image: image,
labels: toKeyValue(labels),
ports: ports
}
}]);
}, []));
});

View File

@ -69,12 +69,10 @@ exports.fromService = function ({ service, instances, packages }) {
deploymentGroupId: service.deployment_group_id,
name: service.name,
slug: service.slug,
environment: service.environment || [],
instances,
currentMetrics: [],
connections: service.service_dependency_ids,
package: packages ? exports.fromPackage(packages) : {},
parent: service.parent_id || '',
parent: service.parent_id,
config: service.config ? service.config : undefined,
status: service.status,
hasPlan: service.has_plan
};
@ -88,15 +86,14 @@ exports.toService = function (clientService) {
deployment_group_id: clientService.deploymentGroupId,
name: clientService.name,
slug: clientService.slug,
environment: clientService.environment,
instance_ids: clientService.instances ?
clientService.instances.map((instance) => {
return instance.id;
}) :
undefined,
service_dependency_ids: clientService.connections,
package_id: clientService.package ? clientService.package.id : undefined,
parent_id: clientService.parent ? clientService.parent : undefined,
config: clientService.config ? clientService.config : undefined,
status: clientService.status,
has_plan: clientService.hasPlan
});
@ -133,6 +130,7 @@ exports.toManifest = function (clientManifest) {
created: clientManifest.created || Date.now(),
type: clientManifest.type,
format: clientManifest.format,
environment: clientManifest.environment,
raw: clientManifest.raw,
json: clientManifest.json || Yamljs.parse(clientManifest.raw)
};
@ -145,6 +143,7 @@ exports.fromManifest = function (manifest) {
created: manifest.created,
type: manifest.type,
format: manifest.format,
environment: manifest.environment,
raw: manifest.raw,
json: manifest.json
};

View File

@ -123,8 +123,8 @@ acorn@^4.0.3:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
acorn@^5.0.0, acorn@^5.0.1, acorn@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d"
version "5.1.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.1.tgz#53fe161111f912ab999ee887a90a0bc52822fd75"
add-stream@^1.0.0:
version "1.0.0"
@ -403,8 +403,8 @@ arr-exclude@^1.0.0:
resolved "https://registry.yarnpkg.com/arr-exclude/-/arr-exclude-1.0.0.tgz#dfc7c2e552a270723ccda04cf3128c8cbfe5c631"
arr-flatten@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.3.tgz#a274ed85ac08849b6bd7847c4580745dc51adfb1"
version "1.1.0"
resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
arr-union@^3.0.0:
version "3.1.0"
@ -497,6 +497,10 @@ ast-types@0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.0.tgz#c8721c8747ae4d5b29b929e99c5317b4e8745623"
ast-types@0.9.6:
version "0.9.6"
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9"
async-each@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
@ -2902,6 +2906,10 @@ deep-extend@~0.4.0:
version "0.4.2"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
deep-get-set@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/deep-get-set/-/deep-get-set-1.1.0.tgz#47bba2008a3fde9c753dcbe61a36241606d9836f"
deep-is@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
@ -2986,8 +2994,8 @@ diff-match-patch@^1.0.0:
resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.0.tgz#1cc3c83a490d67f95d91e39f6ad1f2e086b63048"
diff@3.x.x, diff@^3.0.0, diff@^3.0.1, diff@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
version "3.3.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.0.tgz#056695150d7aa93237ca7e378ac3b1682b7963b9"
diffie-hellman@^5.0.0:
version "5.0.2"
@ -3329,7 +3337,7 @@ eslint-import-resolver-node@^0.3.1:
debug "^2.6.8"
resolve "^1.2.0"
eslint-module-utils@^2.0.0:
eslint-module-utils@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz#abaec824177613b8a95b299639e1b6facf473449"
dependencies:
@ -3352,15 +3360,15 @@ eslint-plugin-hapi@4.x.x:
no-arrowception "1.x.x"
eslint-plugin-import@^2.3.0:
version "2.6.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.6.1.tgz#f580be62bb809421d46e338372764afcc9f59bf6"
version "2.7.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.7.0.tgz#21de33380b9efb55f5ef6d2e210ec0e07e7fa69f"
dependencies:
builtin-modules "^1.1.1"
contains-path "^0.1.0"
debug "^2.6.8"
doctrine "1.5.0"
eslint-import-resolver-node "^0.3.1"
eslint-module-utils "^2.0.0"
eslint-module-utils "^2.1.1"
has "^1.0.1"
lodash.cond "^4.3.0"
minimatch "^3.0.3"
@ -3498,7 +3506,7 @@ esprima@^2.6.0, esprima@~2.7.1:
version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
esprima@^3.1.1:
esprima@^3.1.1, esprima@~3.1.0:
version "3.1.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
@ -4437,6 +4445,10 @@ hoist-non-react-statics@^1.0.3, hoist-non-react-statics@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb"
hoist-non-react-statics@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.0.0.tgz#843180515e0281952b08f41c620ca74870c7e354"
home-or-tmp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
@ -5385,8 +5397,8 @@ json5@0.5.x, json5@^0.5.0, json5@^0.5.1:
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
jsonfile@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.0.tgz#92e7c7444e5ffd5fa32e6a9ae8b85034df8347d0"
version "3.0.1"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66"
optionalDependencies:
graceful-fs "^4.1.6"
@ -5522,8 +5534,8 @@ lerna-wizard@ramitos/lerna-wizard#7bcdc11:
inquirer "^3.0.2"
lerna@^2.0.0-rc.5:
version "2.0.0-rc.5"
resolved "https://registry.yarnpkg.com/lerna/-/lerna-2.0.0-rc.5.tgz#b59d168caaac6e3443078c1bce194208c9aa3090"
version "2.0.0"
resolved "https://registry.yarnpkg.com/lerna/-/lerna-2.0.0.tgz#49a72fe70e06aebfd7ea23efb2ab41abe60ebeea"
dependencies:
async "^1.5.0"
chalk "^1.1.1"
@ -5706,6 +5718,10 @@ lodash.difference@^4.3.0, lodash.difference@^4.4.0, lodash.difference@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c"
lodash.differenceby@^4.8.0:
version "4.8.0"
resolved "https://registry.yarnpkg.com/lodash.differenceby/-/lodash.differenceby-4.8.0.tgz#cfd59e94353af5de51da5d302ca4ebff33faac57"
lodash.find@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1"
@ -7250,13 +7266,13 @@ rc@1.1.7, rc@^1.0.1, rc@^1.1.6, rc@^1.1.7:
strip-json-comments "~2.0.1"
react-apollo@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/react-apollo/-/react-apollo-1.4.2.tgz#e8ee035310b564fd1b42ee7e4f116086b5b06834"
version "1.4.3"
resolved "https://registry.yarnpkg.com/react-apollo/-/react-apollo-1.4.3.tgz#be18bfe7f6609263f3ff623308f88108152a7573"
dependencies:
apollo-client "^1.4.0"
graphql-anywhere "^3.0.0"
graphql-tag "^2.0.0"
hoist-non-react-statics "^1.2.0"
hoist-non-react-statics "^2.0.0"
invariant "^2.2.1"
lodash.flatten "^4.2.0"
lodash.isequal "^4.1.1"
@ -7351,6 +7367,12 @@ react-router@^4.1.1:
prop-types "^15.5.4"
warning "^3.0.0"
react-simple-table@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/react-simple-table/-/react-simple-table-1.0.1.tgz#941390bd68eca63d028c1873bd90772a21143b9c"
dependencies:
deep-get-set "^1.0.0"
react-styled-flexboxgrid@^2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/react-styled-flexboxgrid/-/react-styled-flexboxgrid-2.0.3.tgz#308a8bbc80b1737a65f4ccf35d02afe20932a2f2"
@ -7529,7 +7551,7 @@ readline2@^1.0.1:
is-fullwidth-code-point "^1.0.0"
mute-stream "0.0.5"
recast@0.11.12, recast@^0.11.5:
recast@0.11.12:
version "0.11.12"
resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.12.tgz#a79e4d3f82d5d72a82ee177aeaa791e793bbe5d6"
dependencies:
@ -7538,6 +7560,15 @@ recast@0.11.12, recast@^0.11.5:
private "~0.1.5"
source-map "~0.5.0"
recast@^0.11.5:
version "0.11.23"
resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz#451fd3004ab1e4df9b4e4b66376b2a21912462d3"
dependencies:
ast-types "0.9.6"
esprima "~3.1.0"
private "~0.1.5"
source-map "~0.5.0"
rechoir@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
@ -8763,6 +8794,12 @@ supports-color@^3.1.0, supports-color@^3.1.2, supports-color@^3.2.3:
dependencies:
has-flag "^1.0.0"
supports-color@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.1.0.tgz#92cc14bb3dad8928ca5656c33e19a19f20af5c7a"
dependencies:
has-flag "^2.0.0"
svg-tags@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
@ -9502,17 +9539,18 @@ vfile-location@^2.0.0:
resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-2.0.1.tgz#0bf8816f732b0f8bd902a56fda4c62c8e935dc52"
vfile-reporter@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/vfile-reporter/-/vfile-reporter-3.0.0.tgz#fe50714e373e0d2940510038a99bd609bdc8209f"
version "3.1.0"
resolved "https://registry.yarnpkg.com/vfile-reporter/-/vfile-reporter-3.1.0.tgz#a9b398c5e6dcbc8a9a08e6cf425f092e86a37000"
dependencies:
chalk "^1.1.0"
log-symbols "^1.0.2"
plur "^2.0.0"
repeat-string "^1.5.0"
string-width "^1.0.0"
strip-ansi "^3.0.1"
trim "0.0.1"
supports-color "^4.1.0"
unist-util-stringify-position "^1.0.0"
vfile-statistics "^1.0.0"
vfile-statistics@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/vfile-statistics/-/vfile-statistics-1.0.0.tgz#003d356c0f3e0233f3a2271c65e2acee5ed4d0f8"
vfile@^2.0.0:
version "2.1.0"