diff --git a/packages/cp-frontend/package.json b/packages/cp-frontend/package.json
index c0a493ea..21c28d37 100644
--- a/packages/cp-frontend/package.json
+++ b/packages/cp-frontend/package.json
@@ -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",
diff --git a/packages/cp-frontend/src/components/deployment-groups/create.js b/packages/cp-frontend/src/components/deployment-groups/create.js
index e45292f4..f715c30c 100644
--- a/packages/cp-frontend/src/components/deployment-groups/create.js
+++ b/packages/cp-frontend/src/components/deployment-groups/create.js
@@ -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 }) =>
;
+const EEditor = ManifestEditor => ({ input, defaultValue }) =>
+ ;
+
export const Name = ({ handleSubmit, onCancel, dirty }) =>
;
-export const Manifest = ({
- handleSubmit,
- onCancel,
- dirty,
- defaultValue,
- mode,
- loading
-}) =>
+export const Manifest = ({ handleSubmit, onCancel, dirty, defaultValue }) =>
;
+
+export const Environment = ({
+ handleSubmit,
+ onCancel,
+ dirty,
+ defaultValue,
+ loading
+}) =>
+ ;
export const Review = ({ handleSubmit, onCancel, dirty, ...state }) => {
- const serviceList = forceArray(state.services).map(({ name, image }) =>
+ const serviceList = forceArray(state.services).map(({ name, config }) =>
- {name}
- - Image: {image}
+ - Image: {config.image}
+ {config.environment.length
+ ? - Environment:
+ : undefined}
+ {config.environment.length
+ ?
+ : undefined}
);
@@ -136,7 +177,7 @@ export const Progress = ({ stage, create, edit }) => {
;
- 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
+
+
+ );
+
+ const _environmentCompleted = stage === 'review';
+ const _environmentActive = stage === 'environment';
+
+ const _environment = (
+
+
+ Define Environment
);
@@ -156,7 +212,7 @@ export const Progress = ({ stage, create, edit }) => {
const _review = (
-
+
Review and deploy
@@ -166,6 +222,7 @@ export const Progress = ({ stage, create, edit }) => {
{_name}
{_manifest}
+ {_environment}
{_review}
);
diff --git a/packages/cp-frontend/src/components/services/list-item.js b/packages/cp-frontend/src/components/services/list-item.js
index 04c362c2..088c1ab8 100644
--- a/packages/cp-frontend/src/components/services/list-item.js
+++ b/packages/cp-frontend/src/components/services/list-item.js
@@ -96,6 +96,7 @@ const ServiceListItem = ({
;
+
const view = children
?
{children}
diff --git a/packages/cp-frontend/src/containers/deployment-groups/edit-or-create.js b/packages/cp-frontend/src/containers/deployment-groups/edit-or-create.js
index bf80d5b5..d5c6600a 100644
--- a/packages/cp-frontend/src/containers/deployment-groups/edit-or-create.js
+++ b/packages/cp-frontend/src/containers/deployment-groups/edit-or-create.js
@@ -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 (
+
);
@@ -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]();
}
}
diff --git a/packages/cp-frontend/src/graphql/DeploymentGroupConfig.gql b/packages/cp-frontend/src/graphql/DeploymentGroupConfig.gql
index 14ea3794..4ba3f526 100644
--- a/packages/cp-frontend/src/graphql/DeploymentGroupConfig.gql
+++ b/packages/cp-frontend/src/graphql/DeploymentGroupConfig.gql
@@ -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
+ }
+ }
}
}
diff --git a/packages/cp-frontend/src/graphql/DeploymentGroupProvision.gql b/packages/cp-frontend/src/graphql/DeploymentGroupProvision.gql
index 57ca3b37..4358e6f2 100644
--- a/packages/cp-frontend/src/graphql/DeploymentGroupProvision.gql
+++ b/packages/cp-frontend/src/graphql/DeploymentGroupProvision.gql
@@ -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
diff --git a/packages/cp-frontend/src/state/selectors.js b/packages/cp-frontend/src/state/selectors.js
index 376cb309..8e04b1e4 100644
--- a/packages/cp-frontend/src/state/selectors.js
+++ b/packages/cp-frontend/src/state/selectors.js
@@ -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,
diff --git a/packages/cp-gql-schema/schema.gql b/packages/cp-gql-schema/schema.gql
index 526f201f..d416929c 100644
--- a/packages/cp-gql-schema/schema.gql
+++ b/packages/cp-gql-schema/schema.gql
@@ -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]
diff --git a/packages/docker-compose-client/lib/index.js b/packages/docker-compose-client/lib/index.js
index f83f323d..0713fb42 100644
--- a/packages/docker-compose-client/lib/index.js
+++ b/packages/docker-compose-client/lib/index.js
@@ -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);
diff --git a/packages/manifest-editor/src/index.js b/packages/manifest-editor/src/index.js
index d113e34f..b6c222e8 100644
--- a/packages/manifest-editor/src/index.js
+++ b/packages/manifest-editor/src/index.js
@@ -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, {
diff --git a/packages/portal-api/lib/data/index.js b/packages/portal-api/lib/data/index.js
index 8eb2f0e0..fd18b7c3 100644
--- a/packages/portal-api/lib/data/index.js
+++ b/packages/portal-api/lib/data/index.js
@@ -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
+ }
}]);
}, []));
});
diff --git a/packages/portal-api/lib/data/transform.js b/packages/portal-api/lib/data/transform.js
index 097deac4..73b03731 100644
--- a/packages/portal-api/lib/data/transform.js
+++ b/packages/portal-api/lib/data/transform.js
@@ -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
};
diff --git a/yarn.lock b/yarn.lock
index a59290e4..c6213755 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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"