From 5ccd873a54a2912ed0bf2b87b9121aa5c8bafa69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Ramos?= Date: Wed, 5 Jul 2017 14:33:16 +0100 Subject: [PATCH] feat: environment stage --- packages/cp-frontend/package.json | 1 + .../components/deployment-groups/create.js | 87 ++++++++++++++---- .../src/components/services/list-item.js | 1 + .../deployment-groups/edit-or-create.js | 56 ++++++++++-- .../src/graphql/DeploymentGroupConfig.gql | 20 ++++- .../src/graphql/DeploymentGroupProvision.gql | 4 +- packages/cp-frontend/src/state/selectors.js | 86 ++++++++++-------- packages/cp-gql-schema/schema.gql | 39 ++++---- packages/docker-compose-client/lib/index.js | 17 ++-- packages/manifest-editor/src/index.js | 4 +- packages/portal-api/lib/data/index.js | 75 ++++++++++++---- packages/portal-api/lib/data/transform.js | 11 ++- yarn.lock | 90 +++++++++++++------ 13 files changed, 353 insertions(+), 138 deletions(-) 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 }) =>
@@ -67,14 +71,7 @@ export const Name = ({ handleSubmit, onCancel, dirty }) => ; -export const Manifest = ({ - handleSubmit, - onCancel, - dirty, - defaultValue, - mode, - loading -}) => +export const Manifest = ({ handleSubmit, onCancel, dirty, defaultValue }) =>
import('joyent-manifest-editor')}> {ManifestEditor => @@ -82,7 +79,33 @@ export const Manifest = ({ ? + : } + + + + + +
; + +export const Environment = ({ + handleSubmit, + onCancel, + dirty, + defaultValue, + loading +}) => +
+ import('joyent-manifest-editor')}> + {ManifestEditor => + ManifestEditor + ? : } @@ -95,11 +118,29 @@ export const Manifest = ({
; 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"