diff --git a/packages/cp-frontend/src/components/services/instance-statuses.js b/packages/cp-frontend/src/components/services/instance-statuses.js index 4e10ad00..a517d58e 100644 --- a/packages/cp-frontend/src/components/services/instance-statuses.js +++ b/packages/cp-frontend/src/components/services/instance-statuses.js @@ -16,12 +16,8 @@ const StyledStatusContainer = styled.div` `; const InstanceStatuses = ({ instanceStatuses }) => { - const statuses = instanceStatuses.map(instanceStatus => { - const { - status, - count - } = instanceStatus; + const { status, count } = instanceStatus; return ( diff --git a/packages/cp-frontend/src/containers/deployment-groups/manifest.js b/packages/cp-frontend/src/containers/deployment-groups/manifest.js index 046ba082..e717bbf1 100644 --- a/packages/cp-frontend/src/containers/deployment-groups/manifest.js +++ b/packages/cp-frontend/src/containers/deployment-groups/manifest.js @@ -35,11 +35,11 @@ const Manifest = ({ deploymentGroup && deploymentGroup.imported && !manifest - ? null - : + ? Since this DeploymentGroup was imported, it doesn't have the initial manifest - ; + + : null; return ( diff --git a/packages/cp-frontend/src/containers/services/list.js b/packages/cp-frontend/src/containers/services/list.js index 581fad46..a0a32695 100644 --- a/packages/cp-frontend/src/containers/services/list.js +++ b/packages/cp-frontend/src/containers/services/list.js @@ -44,7 +44,10 @@ class ServiceList extends Component { startServices } = this.props; - if (loading) { + if ( + loading || + (deploymentGroup.status === 'PROVISIONING' && !services.length) + ) { return ( diff --git a/packages/cp-frontend/src/graphql/DeploymentGroupInfo.gql b/packages/cp-frontend/src/graphql/DeploymentGroupInfo.gql index fe39b2f4..7a94c0bb 100644 --- a/packages/cp-frontend/src/graphql/DeploymentGroupInfo.gql +++ b/packages/cp-frontend/src/graphql/DeploymentGroupInfo.gql @@ -3,4 +3,5 @@ fragment DeploymentGroupInfo on DeploymentGroup { name slug imported + status } diff --git a/packages/cp-frontend/src/graphql/DeploymentGroupProvision.gql b/packages/cp-frontend/src/graphql/DeploymentGroupProvision.gql index 2f1357e5..57ca3b37 100644 --- a/packages/cp-frontend/src/graphql/DeploymentGroupProvision.gql +++ b/packages/cp-frontend/src/graphql/DeploymentGroupProvision.gql @@ -5,12 +5,9 @@ mutation provisionManifest($deploymentGroupId: ID!, $type: ManifestType!, $forma replicas } plan { - running - actions { - type - service - machines - } + type + service + machines } } } diff --git a/packages/cp-frontend/src/graphql/ServiceInfo.gql b/packages/cp-frontend/src/graphql/ServiceInfo.gql index 1c53e36e..471adcfc 100644 --- a/packages/cp-frontend/src/graphql/ServiceInfo.gql +++ b/packages/cp-frontend/src/graphql/ServiceInfo.gql @@ -2,4 +2,5 @@ fragment ServiceInfo on Service { id name slug + status } diff --git a/packages/cp-gql-schema/schema.gql b/packages/cp-gql-schema/schema.gql index e82e73e4..526f201f 100644 --- a/packages/cp-gql-schema/schema.gql +++ b/packages/cp-gql-schema/schema.gql @@ -16,6 +16,14 @@ type User { login: String! } +enum DeploymentGroupStatus { + ACTIVE + PROVISIONING + DELETING + DELETED + UNKNOWN +} + type DeploymentGroup { id: ID! name: String! @@ -24,6 +32,7 @@ type DeploymentGroup { version: Version history: [Version] imported: Boolean + status: DeploymentGroupStatus } type ServiceScale { @@ -35,27 +44,25 @@ enum ConvergenceActionType { NOOP CREATE RECREATE + REMOVE START + EXISTING # special status to mark existing ids in previous version } type ConvergenceAction { - id: ID! type: ConvergenceActionType! service: String! # service name - machines: [String]! # instance machine ids -} - -type StateConvergencePlan { - id: ID! - running: Boolean! - actions(type: ConvergenceActionType, service: String): [ConvergenceAction]! + toProcess: Int, # merely used for book keeping + processed: [String], # merely used for book keeping + machines: [String]! # current instance machine ids } type Version { - created: Date! # Either Int or define scalar manifest: Manifest! scale(serviceName: String): [ServiceScale]! - plan(running: Boolean): StateConvergencePlan + plan: [ConvergenceAction] + hasPlan: Boolean + error: String } enum ManifestType { @@ -70,13 +77,24 @@ enum ManifestFormat { type Manifest { id: ID! - created: Float type: ManifestType! format: ManifestFormat! raw: String! obj: Object } +enum ServiceStatus { + ACTIVE # this doesn't mean that the instances are all running + PROVISIONING + SCALING + STOPPING + STOPPED + DELETING + DELETED + RESTARTING + UNKNOWN +} + # immutable type Service { id: ID! # unique id for db row @@ -84,14 +102,12 @@ type Service { name: String! # human readable name slug: String! instances(name: String, machineId: ID, status: InstanceStatus): [Instance]! - # metrics: [MetricType] - currentMetrics: [CurrentMetric] connections: [String!] # list of serviceIds parent: ID # parent service id package: Package! # we don't have this in current mock data, environment: [Environment] - active: Boolean! # let's say that we update the manifest, and remove a service. we still want to track this service even though it might not exist because it might have machines running still image: String # used only for config + status: ServiceStatus } # for metrics max / min (I guess) @@ -135,7 +151,6 @@ type Instance { machineId: ID! status: InstanceStatus! healthy: Boolean - # metrics: [InstanceMetric]! } type Datacenter { @@ -145,28 +160,6 @@ type Datacenter { region: String! } -type InstanceMetric { - type: MetricType! - data: [MetricData]! -} - -type CurrentMetric { - name: String! - value: Float! - measurement: String! -} - -type MetricType { - id: ID! - name: String! - id: ID! -} - -type MetricData { - timestamp: Int! - value: Float! -} - # we probably wont use some of these queries or arguments # but this way we expose the entire db through gql type Query { @@ -178,8 +171,6 @@ type Query { serviceScale(id: ID!): ServiceScale convergenceActions(type: ConvergenceActionType, service: String, versionId: ID): [ConvergenceAction] convergenceAction(id: ID!): ConvergenceAction - stateConvergencePlans(running: Boolean, versionId: ID): [StateConvergencePlan] - stateConvergencePlan(id: ID!): StateConvergencePlan versions(manifestId: ID, deploymentGroupId: ID): [Version] version(id: ID, manifestId: ID): Version manifests(type: String, deploymentGroupId: ID): [Manifest] diff --git a/packages/eslint-config/index.js b/packages/eslint-config/index.js index d7fe165d..9c218ee1 100644 --- a/packages/eslint-config/index.js +++ b/packages/eslint-config/index.js @@ -2,7 +2,13 @@ // TODO wait for eslint/eslint#3458 module.exports = { - extends: ['eslint:recommended', 'xo-space/esnext', 'react-app', 'prettier', 'prettier/react'], + extends: [ + 'eslint:recommended', + 'xo-space/esnext', + 'react-app', + 'prettier', + 'prettier/react' + ], rules: { 'capitalized-comments': 0 } diff --git a/packages/portal-api/lib/data/index.js b/packages/portal-api/lib/data/index.js index 3fda23f4..8eb2f0e0 100644 --- a/packages/portal-api/lib/data/index.js +++ b/packages/portal-api/lib/data/index.js @@ -15,6 +15,10 @@ 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'); +const Flatten = require('lodash.flatten'); +const ForceArray = require('force-array'); const Uuid = require('uuid/v4'); const VAsync = require('vasync'); @@ -29,6 +33,9 @@ const NON_IMPORTABLE_STATES = [ 'FAILED' ]; +const NEW_INSTANCE_ID = '__NEW__'; +const UNKNOWN_INSTANCE_ID = '__UNKNOWN__'; + const internals = { defaults: { name: 'portal', @@ -60,7 +67,7 @@ const internals = { }; -module.exports = class Data extends EventEmitter { +class Data extends EventEmitter { constructor (options) { super(); @@ -107,6 +114,18 @@ module.exports = class Data extends EventEmitter { } + // triton + + _listMachines (deploymentGroupName, cb) { + this._triton.listMachines({ + limit: 9999, + tag: { + [DEPLOYMENT_GROUP]: deploymentGroupName + } + }, cb); + } + + // portals createPortal (clientPortal, cb) { @@ -253,30 +272,40 @@ module.exports = class Data extends EventEmitter { }); } - updateDeploymentGroup ({ id, name }, cb) { - this._db.deployment_groups.update([{ id, name }], (err) => { + updateDeploymentGroup (clientDeploymentGroup, cb) { + this._db.deployment_groups.update([Transform.toDeploymentGroup(clientDeploymentGroup)], (err) => { if (err) { return cb(err); } - cb(null, Transform.fromDeploymentGroup({ id, name })); + this.getDeploymentGroup({ id: clientDeploymentGroup.id }, cb); }); } - _getDeploymentGroupVersion (deploymentGroup) { - const getServices = (args) => { + _getDeploymentGroupFns (deploymentGroup) { + const getServices = (args, cb) => { args = args || {}; - args.deploymentGroupId = deploymentGroup.id; + args.ids = deploymentGroup.service_ids; + + if (typeof cb === 'function') { + return this.getServices(args, cb); + } return new Promise((resolve, reject) => { this.getServices(args, internals.resolveCb(resolve, reject)); }); }; - const getVersion = (args) => { + const getVersion = (args, cb) => { args = args || {}; args.id = deploymentGroup.version_id; + if (typeof cb === 'function') { + return deploymentGroup.version_id ? + this.getVersion(args, cb) : + cb(null); + } + return new Promise((resolve, reject) => { return deploymentGroup.version_id ? this.getVersion(args, internals.resolveCb(resolve, reject)) : @@ -284,9 +313,23 @@ module.exports = class Data extends EventEmitter { }); }; + const getHistory = (args, cb) => { + args = args || {}; + args.version_ids = ForceArray(deploymentGroup.history_version_ids); + + if (typeof cb === 'function') { + return this.getHistory(args, cb); + } + + return new Promise((resolve, reject) => { + return this.getHistory(args, internals.resolveCb(resolve, reject)); + }); + }; + return Object.assign(deploymentGroup, { services: getServices, - version: getVersion + version: getVersion, + history: getHistory }); } @@ -300,9 +343,9 @@ module.exports = class Data extends EventEmitter { return cb(null, []); } - // todo getHistory - - cb(null, deploymentGroups.map((dg) => { return Transform.fromDeploymentGroup(this._getDeploymentGroupVersion(dg)); })); + cb(null, deploymentGroups.map((dg) => { + return Transform.fromDeploymentGroup(this._getDeploymentGroupFns(dg)); + })); }; if (ids) { @@ -332,13 +375,13 @@ module.exports = class Data extends EventEmitter { return cb(null, {}); } - // todo getHistory - - cb(null, Transform.fromDeploymentGroup(this._getDeploymentGroupVersion(deploymentGroups[0]))); + cb(null, Transform.fromDeploymentGroup(this._getDeploymentGroupFns(deploymentGroups[0]))); }); } - _versionManifest (version) { + // versions + + _versionFns (version) { return Object.assign(version, { manifest: (args) => { return new Promise((resolve, reject) => { @@ -350,14 +393,12 @@ module.exports = class Data extends EventEmitter { }); } - // versions - createVersion (clientVersion, cb) { Hoek.assert(clientVersion, 'version is required'); Hoek.assert(clientVersion.manifest, 'manifest is required'); Hoek.assert(clientVersion.deploymentGroupId, 'deploymentGroupId is required'); - console.log(`-> creating new Version for DeploymentGroup ${clientVersion.deploymentGroupId}`); + console.log(`-> creating new Version for DeploymentGroup ${clientVersion.deploymentGroupId}: ${Util.inspect(clientVersion)}`); const version = Transform.toVersion(clientVersion); this._db.versions.insert(version, (err, key) => { @@ -366,41 +407,41 @@ module.exports = class Data extends EventEmitter { } console.log(`-> new Version for DeploymentGroup ${clientVersion.deploymentGroupId} created: ${key}`); - - const changes = { - id: clientVersion.deploymentGroupId, - version_id: key, - history_version_ids: this._db.append(key) - }; - - if (clientVersion.serviceIds) { - changes['service_ids'] = clientVersion.serviceIds; - } - - console.log(`-> updating DeploymentGroup ${clientVersion.deploymentGroupId} to add Version ${key}`); - - this._db.deployment_groups.update([changes], (err) => { + this._db.deployment_groups.query({ + id: clientVersion.deploymentGroupId + }, (err, deploymentGroup) => { if (err) { return cb(err); } - version.id = key; - cb(null, Transform.fromVersion(this._versionManifest(version))); + const changes = { + id: clientVersion.deploymentGroupId, + version_id: key, + history_version_ids: deploymentGroup.version_id ? + ForceArray(deploymentGroup.history_version_ids).concat([]) : + [] + }; + + console.log(`-> updating DeploymentGroup ${clientVersion.deploymentGroupId} to add Version ${key}`); + + this._db.deployment_groups.update([changes], (err) => { + if (err) { + return cb(err); + } + + this.getVersion({ id: key }, cb); + }); }); }); } updateVersion (clientVersion, cb) { - this._db.versions.update([Transform.toVersion(clientVersion)], (err, versions) => { + this._db.versions.update([Transform.toVersion(clientVersion)], (err) => { if (err) { return cb(err); } - if (!versions || !versions.length) { - return cb(null, null); - } - - cb(null, Transform.fromVersion(this._versionManifest(versions[0]))); + this.getVersion({ id: clientVersion.id }, cb); }); } @@ -415,7 +456,7 @@ module.exports = class Data extends EventEmitter { return cb(null, null); } - cb(null, Transform.fromVersion(this._versionManifest(version))); + cb(null, Transform.fromVersion(this._versionFns(version))); }); } @@ -426,7 +467,9 @@ module.exports = class Data extends EventEmitter { } versions = versions || []; - cb(null, versions.map((version) => { return Transform.fromVersion(this._versionManifest(version)); })); + cb(null, versions.map((version) => { + return Transform.fromVersion(this._versionFns(version)); + })); }; // ensure the data is in sync @@ -445,128 +488,353 @@ module.exports = class Data extends EventEmitter { }); } + getHistory ({ version_ids }, cb) { + this._db.services.get(version_ids, (err, versions) => { + if (err) { + return cb(err); + } + + if (!versions || !versions.length) { + return cb(null, []); + } + + cb(null, versions.map((version) => { + return Transform.fromVersion(this._versionFns(version)); + })); + }); + } + + _calcCurrentScale ({ config, currentVersion }, cb) { + return config.map(({ name }) => { + const currentScale = Find(ForceArray(currentVersion.scale), [ + 'serviceName', + name + ]); + + return { + serviceName: name, + replicas: Number.isFinite(currentScale) ? currentScale : 1 + }; + }); + } + + _getCurrentScale ({ deploymentGroupName, config, currentVersion }, cb) { + const fallback = (err) => { + if (err) { + console.error(err); + } + + this._calcCurrentScale({ config, currentVersion }, cb); + }; + + if (!this._triton) { + return fallback(); + } + + const handleMachinesList = (err, machines) => { + if (err) { + return fallback(err); + } + + const liveServices = ForceArray(machines).reduce((acc, { tags }) => { + return Object.assign(acc, { + [tags[SERVICE]]: 1 + }); + }, {}); + + const allAndConfigServices = config.reduce((acc, { name }) => { + return Object.assign(acc, { + [name]: 1 + }); + }, liveServices); + + const scale = Object.keys(allAndConfigServices).map((name) => { + const existingMachines = ForceArray(machines).filter((machine) => { + return machine.tags[SERVICE] === name; + }); + + return { + serviceName: name, + replicas: existingMachines.length ? existingMachines.length : 1 + }; + }); + + cb(null, scale); + }; + + this._listMachines(deploymentGroupName, handleMachinesList); + } + scale ({ serviceId, 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'); - // get the service then get the deployment group + // get the service + // check service status + // update service status + // get the deployment group // use the deployment group to find the current version and manifest + // get instances + // get current scale + // identify plan and future scale + // update version + // callback // scale the service - // maybe update the machine ids and instances + + // this._scale({ service, deployment_group, version, manifest, replicas }, cb); + + const ctx = { + isHandled: false + }; console.log('-> scale request received'); - console.log(`-> fetching Service ${serviceId}`); + const handleFailedScale = (err1, cb) => { + if (err1) { + console.error(err1); + } - this._db.services.single({ id: serviceId }, (err, service) => { + this.updateService({ + id: serviceId, + status: 'ACTIVE' + }, (err2) => { + if (err2) { + console.error(err2); + } + + if (typeof cb === 'function') { + cb(err1 || err2); + } + }); + }; + + const handleTriggeredScale = (err) => { + if (err) { + return handleFailedScale(err); + } + + if (ctx.isHandled) { + return; + } + + ctx.isHandled = true; + + console.log(`-> got response from docker-compose to scale ${ctx.service.name} to ${replicas} replicas`); + }; + + const triggerScale = (err, newVersion) => { + if (err) { + return handleFailedScale(err, cb); + } + + console.log('-> new Version created'); + + cb(null, newVersion); + + setImmediate(() => { + console.log(`-> requesting docker-compose to scale ${ctx.service.name} to ${replicas} replicas`); + + this._dockerCompose.scale({ + projectName: ctx.deploymentGroup.name, + services: { + [ctx.service.name]: replicas + }, + manifest: ctx.manifest.raw + }, handleTriggeredScale); + }); + }; + + const getNewScale = () => { + return ctx.currentScale.map(({ serviceName, replicas }) => { + return { + serviceName: serviceName, + replicas: serviceName === ctx.service.name ? + (ctx.serviceScale + ctx.diff) : + replicas + }; + }); + }; + + const handleScaleDown = () => { + const payload = { + manifest: ctx.manifest, + deploymentGroupId: ctx.deploymentGroup.id, + scale: getNewScale(), + plan: [{ + type: 'REMOVE', + service: ctx.service.name, + toProcess: Math.abs(ctx.diff), + machines: ctx.instances.map(({ machineId }) => { + return machineId; + }) + }], + hasPlan: true + }; + + console.log(`-> creating new Version for DOWN scale ${Util.inspect(payload)}`); + + // note: createVersion updates deploymentGroup + this.createVersion(payload, triggerScale); + }; + + const handleScaleUp = () => { + const payload = { + manifest: ctx.manifest, + deploymentGroupId: ctx.deploymentGroup.id, + scale: getNewScale(), + plan: [{ + type: 'CREATE', + service: ctx.service.name, + toProcess: Math.abs(ctx.diff), + machines: ctx.instances.map(({ machineId }) => { + return machineId; + }) + }], + hasPlan: true + }; + + console.log(`-> creating new Version for UP scale ${Util.inspect(payload)}`); + + // note: createVersion updates deploymentGroup + this.createVersion(payload, triggerScale); + }; + + const handleCurrentScale = (err, currentScale) => { + if (err) { + return handleFailedScale(err, cb); + } + + console.log(`-> got current scale ${Util.inspect(currentScale)}`); + + const serviceReplicas = Find(currentScale, ['serviceName', ctx.service.name]).replicas; + const serviceScale = Number.isFinite(serviceReplicas) ? serviceReplicas : 1; + + const diff = replicas - serviceScale; + + if (diff === 0) { + return handleFailedScale(null, cb); + } + + ctx.serviceScale = serviceScale; + ctx.serviceReplicas = serviceReplicas; + ctx.currentScale = currentScale; + ctx.diff = diff; + + return (diff > 0) ? + handleScaleUp() : + handleScaleDown(); + }; + + const handleManifest = (err, manifest) => { + if (err) { + return handleFailedScale(err, cb); + } + + if (!manifest) { + return cb(new Error(`manifest not found for service with service id: ${serviceId}`)); + } + + ctx.manifest = manifest; + + console.log('-> fetching current scale'); + + this._getCurrentScale({ + deploymentGroupName: ctx.deploymentGroup.name, + currentVersion: ctx.version, + config: [{ + name: ctx.service.name + }] + }, handleCurrentScale); + }; + + const handleVersion = (err, version) => { + if (err) { + return handleFailedScale(err, cb); + } + + if (!version) { + return cb(new Error(`Version not found for service with service id: ${serviceId}`)); + } + + ctx.version = version; + + console.log(`-> fetching Manifest ${version.manifest_id}`); + + this._db.manifests.single({ + id: version.manifest_id + }, handleManifest); + }; + + const handleDeploymentGroup = (err, deploymentGroup) => { + if (err) { + return handleFailedScale(err, cb); + } + + if (!deploymentGroup) { + return cb(new Error(`deployment group not found for service with service id: ${serviceId}`)); + } + + ctx.deploymentGroup = deploymentGroup; + + console.log(`-> fetching Version ${ctx.deploymentGroup.version_id}`); + + this._db.versions.single({ + id: deploymentGroup.version_id + }, handleVersion); + }; + + const handleInstances = (err, instances = []) => { + if (err) { + return handleFailedScale(err, cb); + } + + console.log(`-> got ${instances.length} Instances from ${ctx.service.name}`); + + ctx.instances = instances; + + console.log(`-> fetching DeploymentGroup ${ctx.service.deployment_group_id}`); + + this._db.deployment_groups.single({ + id: ctx.service.deployment_group_id + }, handleDeploymentGroup); + }; + + const handleUpdatedService = (err) => { + if (err) { + return handleFailedScale(err, cb); + } + + console.log(`-> fetching Instances from ${ctx.service.name}`); + + this.getInstances({ ids: ctx.service.instance_ids }, handleInstances); + }; + + const handleService = (err, service) => { if (err) { return cb(err); } if (!service) { - return cb(new Error(`service not found for id: ${serviceId}`)); + return cb(new Error(`Service not found for id: ${serviceId}`)); } + if (service.status !== 'ACTIVE') { + return cb(new Error(`Can't scale when the status is "${service.status}"`)); + } + + ctx.service = service; + console.log(`-> fetching DeploymentGroup ${service.deployment_group_id}`); - this._db.deployment_groups.single({ id: service.deployment_group_id }, (err, deployment_group) => { - if (err) { - return cb(err); - } - - if (!deployment_group) { - return cb(new Error(`deployment group not found for service with service id: ${serviceId}`)); - } - - console.log(`-> fetching Version ${deployment_group.version_id}`); - - this._db.versions.single({ id: deployment_group.version_id }, (err, version) => { - if (err) { - return cb(err); - } - - if (!version) { - return cb(new Error(`version not found for service with service id: ${serviceId}`)); - } - - console.log(`-> fetching Manifest ${version.manifest_id}`); - - this._db.manifests.single({ id: version.manifest_id }, (err, manifest) => { - if (err) { - return cb(err); - } - - if (!manifest) { - return cb(new Error(`manifest not found for service with service id: ${serviceId}`)); - } - - this._scale({ service, deployment_group, version, manifest, replicas }, cb); - }); - }); - }); - }); - } - - _scale ({ service, deployment_group, version, manifest, replicas }, cb) { - let isFinished = false; - - const finish = () => { - if (isFinished) { - return; - } - - isFinished = true; - - console.log(`-> docker-compose scaled "${service.name}" from DeploymentGroup ${deployment_group.id} to ${replicas} replicas`); - - if (!version.service_scales || !version.service_scales.length) { - console.log(`-> no scale data found for service "${service.name}" from DeploymentGroup ${deployment_group.id} in current Version (${version.id})`); - - version.service_scales = [{ - service_name: service.name - }]; - } - - const clientVersion = { - deploymentGroupId: deployment_group.id, - manifest, - plan: version.plan, - scale: version.service_scales.map((scale) => { - if (scale.service_name !== service.name) { - return scale; - } - - return { - serviceName: service.name, - replicas - }; - }) - }; - - console.log(`-> creating new Version for DeploymentGroup ${deployment_group.id}`); - - // createVersion updates the deployment group - this.createVersion(clientVersion, (...args) => { - isFinished = true; - cb(...args); - }); + this.updateService({ + id: serviceId, + status: 'SCALING' + }, handleUpdatedService); }; - console.log(`-> requesting docker-compose to scale "${service.name}" from DeploymentGroup ${deployment_group.id} to ${replicas} replicas`); + console.log(`-> fetching Service ${serviceId}`); - this._dockerCompose.scale({ - projectName: deployment_group.name, - services: { - [service.name]: replicas - }, - manifest: manifest.raw - }, (err, res) => { - if (err) { - return cb(err); - } - - finish(); - }); + this._db.services.single({ id: serviceId }, handleService); } @@ -574,147 +842,430 @@ module.exports = class Data extends EventEmitter { provisionManifest (clientManifest, cb) { // 1. check that the deploymentgroup exists - // 2. create a new manifest - // 3. create a new version - // 4. return said version - // 5. request docker-compose-api to provision manifest - // 6. create/update/prune services by calling provisionServices with the respose from docker-compose-api - // 7. update version with the provision plan and new service ids + // 2. update Deployment Group to set PROVISIONING status + // 3. get docker-compose config for the given manifest + // 4. create a new manifest + // 5. fetch current version + // 6. get curent scale based on machines in triton + // 7. create new version + // 8. call `up` and get the response + // 9. iterate over services from provision response + // 10. on each service, either create or update it with new status and hash + // 11. fetch all the existing services + // 12. for each existing service, check if it still exists on this provision. if it doesn't update to mark DELETING + // 13. update deployment group with calculated plan and ACTIVE status - // todo we are not doing anything with the action plans right now - // but if we were, we would do that in portal-watch. with that said, we might - // run into a race condition where the event happens before we update the - // new version with the plan + const ctx = { + isHandled: false + }; console.log('-> provision request received'); - const provision = ({ deploymentGroup, manifest, newVersion }) => { - let isHandled = false; + const handleFailedProvision = (err) => { + if (!err) { + return; + } - console.log(`-> requesting docker-compose provision for DeploymentGroup ${deploymentGroup.name}`); + console.error(err); - this._dockerCompose.provision({ - projectName: deploymentGroup.name, - manifest: clientManifest.raw - }, (err, provisionRes) => { + this.updateVersion({ + id: ctx.newVersion.id, + error: `${err.message}\n${err.stack}` + }, (err) => { if (err) { - this.emit('error', err); - return; + console.error(err); + } + }); + }; + + const ServiceStatusFromPlan = { + NOOP: 'ACTIVE', + CREATE: 'PROVISIONING', + RECREATE: 'PROVISIONING', + START: 'PROVISIONING' + }; + + // 15. handle fetched instantes + // 16. update deployment group with calculated plan and ACTIVE status + const handleServiceInstanceMap = (err, result) => { + if (err) { + return handleFailedProvision(err); + } + + const services = ForceArray(result.successes); + + console.log(`-> got a map of Service's-Instance's from DeploymentGroup ${ctx.currentDeploymentGroup.id} ${Util.inspect(services)}`); + + const plan = Flatten(services.map(({ name, instances }) => { + const provision = ctx.provisionRes[name]; + const machines = instances.map(({ machineId }) => { + return machineId; + }); + + const { replicas } = Find(ctx.currentScale, ['serviceName', name]); + const scale = Number.isFinite(replicas) ? replicas : 1; + const action = Get(provision, 'plan.action', 'NOOP').toUpperCase(); + + if (!provision) { + return { + type: 'REMOVE', + service: name, + toProcess: machines.length, + machines: machines + }; } - // callback can execute multiple times, ensure responses are only handled once - if (isHandled) { - return; - } - - isHandled = true; - - console.log('-> update/create/remove services based on response from docker-compose'); - - // create/update services based on hashes - // return the new set of service ids - this.provisionServices({ - deploymentGroup, - provisionRes - }, (err, newServiceIds) => { - if (err) { - this.emit('error', err); - return; + const ActionMap = { + 'NOOP': () => { + return { + type: 'NOOP', + service: name, + machines + }; + }, + 'CREATE': () => { + return { + type: 'CREATE', + service: name, + toProcess: scale, + machines: machines + }; + }, + 'RECREATE': () => { + return { + type: 'CREATE', + service: name, + toProcess: machines.length, + machines: machines + }; + }, + 'START': () => { + return { + type: 'START', + service: name, + machines + }; } + }; - console.log(`-> update Version ${newVersion.id} based on docker-compose response and new service ids`); + return ActionMap[action](); + })); - const actions = Object.keys(provisionRes).map((serviceName) => { - return ({ - type: provisionRes[serviceName].plan.action, - service: serviceName, - machines: provisionRes[serviceName].plan.containers.map(({ id }) => { return id; }) - }); - }); + VAsync.parallel({ + funcs: [ + (cb) => { + console.log(`-> updating Version ${ctx.newVersion.id} from DeploymentGroup ${ctx.currentDeploymentGroup.id} with new Plan ${Util.inspect(plan)}`); + return this.updateVersion({ + id: ctx.newVersion.id, + hasPlan: true, + plan + }, cb); + }, + (cb) => { + console.log(`-> updating DeploymentGroup ${ctx.currentDeploymentGroup.id} with new Service's ${Util.inspect(ctx.newServices)} and ACTIVE status`); - // create new version - this.updateVersion({ - id: newVersion.id, - manifest, - newServiceIds, - plan: { - running: true, - actions: actions - } - }, (err) => { + const services = UniqBy( + ForceArray(ctx.newServices) + .concat(ForceArray(ctx.previousServices)), + 'id' + ); + + this.updateDeploymentGroup({ + id: ctx.currentDeploymentGroup.id, + status: 'ACTIVE', + services: services + }, cb); + } + ] + }, handleFailedProvision); + }; + + // 14. fetch instanceIds for each Service + const handleRemovedServices = (err) => { + if (err) { + return handleFailedProvision(err); + } + + console.log(`-> marked removed Service's with DELETING from DeploymentGroup ${ctx.currentDeploymentGroup.id}`); + console.log(`-> fetching a map of Service's-Instance's from DeploymentGroup ${ctx.currentDeploymentGroup.id}`); + + VAsync.forEachParallel({ + inputs: ctx.previousServices, + func: (service, next) => { + service.instances({}, (err, instances) => { if (err) { - this.emit('error', err); - return; + return next(err); } - console.log(`-> updated Version ${newVersion.id}`); - console.log('-> provisionManifest DONE'); + next(err, Object.assign({}, service, { + instances + })); }); - }); - }); + } + }, handleServiceInstanceMap); }; - const createVersion = ({ deploymentGroup, currentVersion, manifest }) => { - // create new version - this.createVersion({ - manifest, - deploymentGroupId: deploymentGroup.id, - scale: currentVersion.scale, - plan: { - running: true, - actions: [] + // 13. handle all the existing services response + const handlePreviousServices = (err, previousServices = []) => { + if (err) { + return handleFailedProvision(err); + } + + console.log(`-> identified previous Service's from DeploymentGroup ${ctx.currentDeploymentGroup.id} ${Util.inspect(ctx.previousServices)}`); + + ctx.previousServices = previousServices; + + // 12. for existing service, check if it still exists on this provision. if it doesn't update to mark DELETING + ctx.removedServices = previousServices.filter(({ name }) => { + return !Find(ctx.newServices, ['name', name]); + }); + + console.log(`-> identified removed Service's from DeploymentGroup ${ctx.currentDeploymentGroup.id} ${Util.inspect(ctx.removedServices)}`); + + VAsync.forEachParallel({ + inputs: ctx.removedServices, + func: ({ id, name }, next) => { + console.log(`-> marking Service ${name} as DELETING from DeploymentGroup ${ctx.currentDeploymentGroup.id}`); + this.updateService({ + id, + status: 'DELETING' + }, next); } - }, (err, newVersion) => { + }, handleRemovedServices); + }; + + // 11. fetch all the existing services + const handleNewServices = (err, result) => { + if (err) { + return handleFailedProvision(err); + } + + ctx.newServices = ForceArray(result.successes); + + console.log(`-> got "${ctx.newServices.length}" Services provisioned from DeploymentGroup ${ctx.currentDeploymentGroup.id}`); + + ctx.currentDeploymentGroup.services({}, handlePreviousServices); + }; + + const createProvisionService = ({ payload }, cb) => { + console.log(`-> creating Service "${payload.name}" from DeploymentGroup ${ctx.currentDeploymentGroup.id}`); + this.createService(payload, cb); + }; + + const updateProvisionService = ({ payload, serviceId }, cb) => { + console.log(`-> updating Service "${payload.name}" from DeploymentGroup ${ctx.currentDeploymentGroup.id}`); + this.updateService(Object.assign({}, payload, { + id: serviceId + }), cb); + }; + + // 10. on each service, either create or update it with new status and hash + const handleProvisionService = (serviceName, next) => { + console.log(`-> handling Service "${serviceName}" from DeploymentGroup ${ctx.currentDeploymentGroup.id}`); + + this.getServices({ + name: serviceName, + deploymentGroupId: ctx.currentDeploymentGroup.id + }, (err, services = []) => { if (err) { - return cb(err); + return next(err); } - console.log(`-> new Version created with id ${newVersion.id}`); - console.log('newVersion', newVersion); + console.log(`-> got ${services.length} services with name ${serviceName} from DeploymentGroup ${ctx.currentDeploymentGroup.id}`); - setImmediate(() => { - provision({ deploymentGroup, manifest, newVersion }); - }); + const provision = ctx.provisionRes[serviceName]; + const action = Get(provision, 'plan.action', 'noop').toUpperCase(); + const service = services.shift(); - cb(null, newVersion); + const payload = { + hash: provision.hash, + deploymentGroupId: ctx.currentDeploymentGroup.id, + name: serviceName, + slug: ParamCase(serviceName), + status: ServiceStatusFromPlan[action] + }; + + return !service ? + createProvisionService({ payload }, next) : + updateProvisionService({ payload, serviceId: service.id }, next); }); }; - this.getDeploymentGroup({ - id: clientManifest.deploymentGroupId - }, (err, deploymentGroup) => { + // 8. handle `up` response + // 9. asynchronously iterate over services from provision response + const handleProvisionResponse = (err, provisionRes) => { + if (err) { + return handleFailedProvision(err); + } + + if (ctx.isHandled) { + return; + } + + console.log(`-> got response from provision ${Util.inspect(provisionRes)}`); + + ctx.isHandled = true; + ctx.provisionRes = provisionRes; + + VAsync.forEachParallel({ + inputs: Object.keys(ctx.provisionRes), + func: handleProvisionService + }, handleNewServices); + }; + + // 7. handle new version + // 8. call docker-compose to up dg + const handleNewVersion = (err, newVersion) => { if (err) { return cb(err); } - if (!deploymentGroup) { + // note: deployment group is updated when version is created + + ctx.newVersion = newVersion; + + // cb with new version + // CALLBACK + cb(null, ctx.newVersion); + + setImmediate(() => { + console.log(`-> requesting docker-compose provision for DeploymentGroup ${ctx.currentDeploymentGroup.name}`); + + this._dockerCompose.provision({ + projectName: ctx.currentDeploymentGroup.name, + manifest: ctx.newManifest.raw + }, handleProvisionResponse); + }); + }; + + // 6. handle curent scale based on machines in triton + // 7. create new version + const handleCurrentScale = (err, currentScale) => { + if (err) { + return cb(err); + } + + console.log(`-> got current scale ${Util.inspect(currentScale)}`); + + ctx.currentScale = currentScale; + + this.createVersion({ + manifest: ctx.newManifest, + deploymentGroupId: ctx.currentDeploymentGroup.id, + scale: currentScale, + plan: [], + hasPlan: false + }, handleNewVersion); + }; + + // 5. handle current version + // 6. get curent scale based on machines in triton + const handleCurrentVersion = (err, currentVersion) => { + if (err) { + return cb(err); + } + + if (!currentVersion) { + console.log(`-> detected first provision for DeploymentGroup ${ctx.currentDeploymentGroup.id}`); + } else { + console.log(`-> creating new Version based on old Version ${currentVersion.id}`); + } + + ctx.currentVersion = currentVersion; + + this._getCurrentScale({ + deploymentGroupName: ctx.currentDeploymentGroup.name, + config: ctx.config, + currentVersion + }, handleCurrentScale); + }; + + // 4. handle new version + // 5. fetch current version + const handleNewManifest = (err, newManifest) => { + if (err) { + return cb(err); + } + + console.log(`-> fetching current version for ${ctx.currentDeploymentGroup.id}`); + + ctx.newManifest = newManifest; + ctx.currentDeploymentGroup.version(null, handleCurrentVersion); + }; + + // 3. handle docker-compose config for the given manifest + // 4. create a new manifest + const handleConfig = (err, config) => { + if (err) { + return cb(err); + } + + console.log(`-> got docker-compose config ${Util.inspect(config)}`); + + ctx.config = config; + + this.createManifest(clientManifest, handleNewManifest); + }; + + // 1. check if deployment group exists + // 2. update Deployment Group to set PROVISIONING status + // 3. get docker-compose config for the given manifest + const handleDeploymentGroup = (err, currentDeploymentGroup) => { + if (err) { + return cb(err); + } + + if (!currentDeploymentGroup) { return cb(new Error('Deployment group not found for manifest')); } - console.log(`-> new DeploymentGroup created with id ${deploymentGroup.id}`); + if (currentDeploymentGroup.status !== 'ACTIVE') { + console.error(`-> Can't provision when the status is "${currentDeploymentGroup.status}"`); + // return last version + return currentDeploymentGroup.version({}, cb); + } - const newManifest = Transform.toManifest(clientManifest); - this._db.manifests.insert(newManifest, (err, manifestId) => { + console.log(`-> DeploymentGroup found with id ${currentDeploymentGroup.id}`); + + const configPayload = Object.assign({}, clientManifest, { + deploymentGroupName: currentDeploymentGroup.name + }); + + console.log(`-> requesting docker-compose config for manifest ${Util.inspect(configPayload)}`); + + ctx.currentDeploymentGroup = currentDeploymentGroup; + + this.updateDeploymentGroup({ + id: ctx.currentDeploymentGroup.id, + status: 'PROVISIONING' + }, (err) => { if (err) { return cb(err); } - console.log(`-> new Manifest created with id ${manifestId}`); - - deploymentGroup.version().then((currentVersion) => { - if (!currentVersion) { - console.log(`-> detected first provision for DeploymentGroup ${deploymentGroup.id}`); - } else { - console.log(`-> creating new Version based on old version ${currentVersion.id}`); - } - - return createVersion({ - deploymentGroup, - manifest: { id: manifestId }, - currentVersion: currentVersion || {} - }); - }).catch((err) => { return cb(err); }); + this.getConfig(configPayload, handleConfig); }); + }; + + // 1. fetch current deployment group + this.getDeploymentGroup({ + id: clientManifest.deploymentGroupId + }, handleDeploymentGroup); + } + + createManifest (clientManifest, cb) { + console.log(`-> creating new Manifest ${Util.inspect(clientManifest)}`); + + const newManifest = Transform.toManifest(clientManifest); + this._db.manifests.insert(newManifest, (err, manifestId) => { + if (err) { + return cb(err); + } + + console.log(`-> new Manifest created with id ${manifestId}`); + + clientManifest.id = manifestId; + cb(null, Transform.fromManifest(clientManifest)); }); } @@ -740,139 +1291,6 @@ module.exports = class Data extends EventEmitter { }); } - - // services - - provisionServices ({ deploymentGroup, provisionRes }, cb) { - // 1. get current set of services - // 2. compare names and hashes - // 3. if name doesn't exist anymore, disable service - // 4. if hash is new, update service - // 5. compare previous services with new ones - // 6. deactivate pruned ones - - console.log('-> provision services in our data layer'); - - const createService = ({ provision, serviceName }, cb) => { - console.log(`-> creating Service "${serviceName}" from DeploymentGroup ${deploymentGroup.id}`); - - this.createService({ - hash: provision.hash, - deploymentGroupId: deploymentGroup.id, - name: serviceName, - slug: ParamCase(serviceName) - }, (err, service) => { - if (err) { - return cb(err); - } - - cb(null, service.id); - }); - }; - - const updateService = ({ provision, service }, cb) => { - console.log(`-> updating Service "${service.name}" from DeploymentGroup ${deploymentGroup.id}`); - - this.updateService({ - id: service.id, - hash: provision.hash - }, (err) => { - if (err) { - return cb(err); - } - - cb(null, service.id); - }); - }; - - const resolveService = (serviceName, next) => { - console.log(`-> fetching Service "${serviceName}" from DeploymentGroup ${deploymentGroup.id}`); - - const provision = provisionRes[serviceName]; - - this.getServices({ - name: serviceName, - deploymentGroupId: deploymentGroup.id - }, (err, services) => { - if (err) { - return cb(err); - } - - // no services for given name - if (!services || !services.length) { - return createService({ provision, serviceName }, next); - } - - const service = services.shift(); - - VAsync.forEachPipeline({ - inputs: services, - // disable old services - func: ({ id }, next) => { - console.log(`-> deactivating Service ${id} from DeploymentGroup ${deploymentGroup.id}`); - this.updateService({ active: false, id }, next); - } - }, (err) => { - if (err) { - return cb(err); - } - - // service changed - if (service.hash !== provision.hash) { - return updateService({ provision, service }, next); - } - - console.log(`-> no changes for Service "${serviceName}" from DeploymentGroup ${deploymentGroup.id}`); - return next(null, service.id); - }); - }); - }; - - const pruneService = ({ id, instances }, next) => { - // if it has instances, just mark as inactive - console.log(`-> pruning Service ${id} from DeploymentGroup ${deploymentGroup.id}`); - - const update = () => { return this.updateService({ active: false, id }, next); }; - const remove = () => { return this.deleteServices({ ids: [id] }, next); }; - - return (instances && instances.length) ? - update() : - remove(); - }; - - // deactivate pruned services - const pruneServices = (err, result) => { - if (err) { - return cb(err); - } - - console.log(`-> pruning Services from DeploymentGroup ${deploymentGroup.id}`); - - const new_service_ids = result.successes; - - this.getServices({ - deploymentGroupId: deploymentGroup.id - }, (err, oldServices) => { - if (err) { - return cb(err); - } - - const servicesToPrune = oldServices - .filter(({ id }) => { return new_service_ids.indexOf(id) < 0; }); - - VAsync.forEachPipeline({ - inputs: servicesToPrune, - func: pruneService - }, (err) => { return cb(err, new_service_ids); }); - }); - }; - - VAsync.forEachPipeline({ - inputs: Object.keys(provisionRes), - func: resolveService - }, pruneServices); - } - createService (clientService, cb) { const newService = Object.assign(Transform.toService(clientService), { active: true @@ -889,37 +1307,39 @@ module.exports = class Data extends EventEmitter { } updateService (clientService, cb) { - this._db.services.update([Transform.toService(clientService)], (err, services) => { + const payload = Transform.toService(clientService); + console.log(`-> got update Service request ${Util.inspect(payload)}`); + + this._db.services.update([payload], (err) => { if (err) { return cb(err); } - if (!services || !services.length) { - return cb(null, null); - } - - cb(null, Transform.fromService(services[0])); + this.getService({ id: clientService.id }, cb); }); } getService ({ id, hash }, cb) { const query = id ? { id } : { version_hash: hash }; - this._db.services.query(query, (err, service) => { + console.log(`-> fetching Service ${Util.inspect(query)}`); + this._db.services.query(query, (err, services) => { if (err) { return cb(err); } - if (!service) { - return cb(null, null); + if (!services || !services.length) { + console.log(`-> Service ${Util.inspect(query)} not found`); + return cb(); } - this._db.packages.single({ id: service.package_id }, (err, packages) => { - if (err) { - return cb(err); - } + const service = services.shift(); - cb(null, Transform.fromService({ service, instances: this._instancesFilter(service.instance_ids), packages })); - }); + console.log(`-> Service ${Util.inspect(query)} found ${Util.inspect(service)}`); + + return cb(null, Transform.fromService({ + service, + instances: this._instancesFilter(service.instance_ids) + })); }); } @@ -978,144 +1398,95 @@ module.exports = class Data extends EventEmitter { }); } - _instancesFilter (instanceIds) { - return (query) => { + _instancesFilter (instanceIds = []) { + return (query, cb) => { query = query || {}; + query.ids = instanceIds; + + if (typeof cb === 'function') { + return instanceIds && instanceIds.length ? + this.getInstances(query, cb) : + cb(null, []); + } return new Promise((resolve, reject) => { - query.ids = instanceIds; - - this.getInstances(query, internals.resolveCb(resolve, reject)); + return instanceIds && instanceIds.length ? + this.getInstances(query, internals.resolveCb(resolve, reject)) : + resolve([]); }); }; } stopServices ({ ids }, cb) { - this._db.services.get(ids, (err, services) => { - if (err) { - return cb(err); + const revertStatus = (err1, cb) => { + if (err1) { + console.error(err1); } - if (!services || !services.length) { - return cb(); - } - - const instanceIds = services.reduce((instanceIds, service) => { - return instanceIds.concat(service.instance_ids); - }, []); - VAsync.forEachParallel({ - func: (instanceId, next) => { - this._db.instances.get(instanceId, (err, instance) => { - if (err) { - return next(err); - } - - if (!this._triton) { - return next(); - } - - this._triton.stopMachine(instance.machine_id, next); - }); - }, - inputs: instanceIds - }, (err, results) => { - if (err) { - return cb(err); + inputs: ids, + func: (serviceId, next) => { + this.updateService({ + id: serviceId, + status: 'ACTIVE' + }, next); + } + }, (err2) => { + if (err2) { + console.error(err2); } - this.getServices({ ids }, cb); + if (cb) { + cb(err1 || err2); + } }); - }); - } + }; - startServices ({ ids }, cb) { - this._db.services.get(ids, (err, services) => { + const handleUpdatedServices = ({ + currentServices + }) => { + return (err, result) => { + if (err) { + return revertStatus(err, cb); + } + + cb(null, result.successes); + + setImmediate(() => { + const instanceIds = currentServices.reduce((instanceIds, service) => { + return instanceIds.concat(service.instance_ids); + }, []); + + VAsync.forEachParallel({ + inputs: instanceIds, + func: (instanceId, next) => { + this._db.instances.get(instanceId, (err, instance) => { + if (err) { + return next(err); + } + + if (!this._triton) { + return next(); + } + + this._triton.stopMachine(instance.machine_id, next); + }); + } + }, (err) => { + if (err) { + console.error(err); + } + }); + }); + }; + }; + + const handleCurrentServices = (err, currentServices) => { if (err) { return cb(err); } - if (!services || !services.length) { - return cb(); - } - - const instanceIds = services.reduce((instanceIds, service) => { - return instanceIds.concat(service.instance_ids); - }, []); - - VAsync.forEachParallel({ - func: (instanceId, next) => { - this._db.instances.get(instanceId, (err, instance) => { - if (err) { - return next(err); - } - - if (!this._triton) { - return next(); - } - - this._triton.startMachine(instance.machine_id, next); - }); - }, - inputs: instanceIds - }, (err, results) => { - if (err) { - return cb(err); - } - - this.getServices({ ids }, cb); - }); - }); - } - - restartServices ({ ids }, cb) { - this._db.services.get(ids, (err, services) => { - if (err) { - return cb(err); - } - - if (!services || !services.length) { - return cb(); - } - - const instanceIds = services.reduce((instanceIds, service) => { - return instanceIds.concat(service.instance_ids); - }, []); - - VAsync.forEachParallel({ - func: (instanceId, next) => { - this._db.instances.get(instanceId, (err, instance) => { - if (err) { - return next(err); - } - - if (!this._triton) { - return next(); - } - - this._triton.rebootMachine(instance.machine_id, next); - }); - }, - inputs: instanceIds - }, (err, results) => { - if (err) { - return cb(err); - } - - this.getServices({ ids }, cb); - }); - }); - } - - deleteServices ({ ids }, cb) { - // todo could this be done with scale = 0? - - this._db.services.get(ids, (err, services) => { - if (err) { - return cb(err); - } - - if (!services || !services.length) { + if (!currentServices || !currentServices.length) { return cb(); } @@ -1124,42 +1495,283 @@ module.exports = class Data extends EventEmitter { func: (serviceId, next) => { this.updateService({ id: serviceId, - active: false + status: 'STOPPING' }, next); } - }, (err) => { - if (err) { - return cb(err); + }, handleUpdatedServices({ + currentServices + })); + }; + + this._db.services.get(ids, handleCurrentServices); + } + + startServices ({ ids }, cb) { + const revertStatus = (err1, cb) => { + if (err1) { + console.error(err1); + } + + VAsync.forEachParallel({ + inputs: ids, + func: (serviceId, next) => { + this.updateService({ + id: serviceId, + status: 'ACTIVE' + }, next); + } + }, (err2) => { + if (err2) { + console.error(err2); } - this.getServices({ ids }, cb); - - const instanceIds = services.reduce((instanceIds, service) => { - return instanceIds.concat(service.instance_ids); - }, []); - - VAsync.forEachParallel({ - func: (instanceId, next) => { - this._db.instances.get(instanceId, (err, instance) => { - if (err) { - return next(err); - } - - if (!this._triton) { - return next(); - } - - this._triton.deleteMachine(instance.machine_id, next); - }); - }, - inputs: instanceIds - }, (err, results) => { - if (err) { - console.error(err); - } - }); + if (cb) { + cb(err1 || err2); + } }); - }); + }; + + const handleUpdatedServices = ({ + currentServices + }) => { + return (err, result) => { + if (err) { + return revertStatus(err, cb); + } + + cb(null, result.successes); + + setImmediate(() => { + const instanceIds = currentServices.reduce((instanceIds, service) => { + return instanceIds.concat(service.instance_ids); + }, []); + + VAsync.forEachParallel({ + inputs: instanceIds, + func: (instanceId, next) => { + this._db.instances.get(instanceId, (err, instance) => { + if (err) { + return next(err); + } + + if (!this._triton) { + return next(); + } + + this._triton.startMachine(instance.machine_id, next); + }); + } + }, (err) => { + if (err) { + console.error(err); + } + }); + }); + }; + }; + + const handleCurrentServices = (err, currentServices) => { + if (err) { + return cb(err); + } + + if (!currentServices || !currentServices.length) { + return cb(); + } + + VAsync.forEachParallel({ + inputs: ids, + func: (serviceId, next) => { + this.updateService({ + id: serviceId, + status: 'ACTIVE' + }, next); + } + }, handleUpdatedServices({ + currentServices + })); + }; + + this._db.services.get(ids, handleCurrentServices); + } + + restartServices ({ ids }, cb) { + // 1. update all services statuses to RESTARTING + // 2. get all instances + // 3. restart all instances + // 4. revert service status + + const revertStatus = (err1, cb) => { + if (err1) { + console.error(err1); + } + + VAsync.forEachParallel({ + inputs: ids, + func: (serviceId, next) => { + this.updateService({ + id: serviceId, + status: 'ACTIVE' + }, next); + } + }, (err2) => { + if (err2) { + console.error(err2); + } + + if (cb) { + cb(err1 || err2); + } + }); + }; + + const handleUpdatedServices = ({ + currentServices + }) => { + return (err, result) => { + if (err) { + return revertStatus(err, cb); + } + + cb(null, result.successes); + + setImmediate(() => { + const instanceIds = currentServices.reduce((instanceIds, service) => { + return instanceIds.concat(service.instance_ids); + }, []); + + VAsync.forEachParallel({ + inputs: instanceIds, + func: (instanceId, next) => { + this._db.instances.get(instanceId, (err, instance) => { + if (err) { + return next(err); + } + + if (!this._triton) { + return next(); + } + + this._triton.rebootMachine(instance.machine_id, next); + }); + } + }, revertStatus); + }); + }; + }; + + const handleCurrentServices = (err, currentServices) => { + if (err) { + return cb(err); + } + + if (!currentServices || !currentServices.length) { + return cb(); + } + + VAsync.forEachParallel({ + inputs: ids, + func: (serviceId, next) => { + this.updateService({ + id: serviceId, + status: 'RESTARTING' + }, next); + } + }, handleUpdatedServices({ + currentServices + })); + }; + + this._db.services.get(ids, handleCurrentServices); + } + + deleteServices ({ ids }, cb) { + const revertStatus = (err1, cb) => { + if (err1) { + console.error(err1); + } + + VAsync.forEachParallel({ + inputs: ids, + func: (serviceId, next) => { + this.updateService({ + id: serviceId, + status: 'ACTIVE' + }, next); + } + }, (err2) => { + if (err2) { + console.error(err2); + } + + if (cb) { + cb(err1 || err2); + } + }); + }; + + const handleUpdatedServices = ({ + currentServices + }) => { + return (err, result) => { + if (err) { + return revertStatus(err, cb); + } + + cb(null, result.successes); + + setImmediate(() => { + const instanceIds = currentServices.reduce((instanceIds, service) => { + return instanceIds.concat(service.instance_ids); + }, []); + + VAsync.forEachParallel({ + inputs: instanceIds, + func: (instanceId, next) => { + this._db.instances.get(instanceId, (err, instance) => { + if (err) { + return next(err); + } + + if (!this._triton) { + return next(); + } + + this._triton.deleteMachine(instance.machine_id, next); + }); + } + }, (err) => { + if (err) { + console.error(err); + } + }); + }); + }; + }; + + const handleCurrentServices = (err, currentServices) => { + if (err) { + return cb(err); + } + + if (!currentServices || !currentServices.length) { + return cb(); + } + + VAsync.forEachParallel({ + inputs: ids, + func: (serviceId, next) => { + this.updateService({ + id: serviceId, + status: 'DELETING' + }, next); + } + }, handleUpdatedServices({ + currentServices + })); + }; + + this._db.services.get(ids, handleCurrentServices); } @@ -1218,21 +1830,23 @@ module.exports = class Data extends EventEmitter { }); } - updateInstance ({ id, status, healthy }, cb) { + updateInstance ({ id, status, healthy}, cb) { const changes = { id }; + if (typeof healthy === 'boolean') { changes.healthy = healthy; } + if (status) { changes.status = status; } - this._db.instances.update([changes], (err, instances) => { + this._db.instances.update([changes], (err) => { if (err) { return cb(err); } - cb(null, instances && instances.length ? Transform.fromInstance(instances[0]) : {}); + this.getInstance({ id }, cb); }); } @@ -1437,16 +2051,25 @@ module.exports = class Data extends EventEmitter { return cb(err); } - const names = dgs.map(({ name }) => { return name; }); + const names = dgs.map(({ name }) => { + return name; + }); return cb( null, UniqBy( machines - .filter(({ tags = {} }) => { return names.indexOf(tags[DEPLOYMENT_GROUP]) < 0; }) - .filter(({ state }) => { return NON_IMPORTABLE_STATES.indexOf(state.toUpperCase()) < 0; }) - .filter(({ tags = {} }) => { return [DEPLOYMENT_GROUP, SERVICE, HASH].every((name) => { return tags[name]; }); } - ) + .filter(({ tags = {} }) => { + return names.indexOf(tags[DEPLOYMENT_GROUP]) < 0; + }) + .filter(({ state }) => { + return NON_IMPORTABLE_STATES.indexOf(state.toUpperCase()) < 0; + }) + .filter(({ tags = {} }) => { + return [DEPLOYMENT_GROUP, SERVICE, HASH].every((name) => { + return tags[name]; + }); + }) .map(({ tags = {} }) => { return ({ id: Uuid(), @@ -1477,10 +2100,14 @@ module.exports = class Data extends EventEmitter { const containers = machines .filter( - ({ tags = {} }) => { return tags[DEPLOYMENT_GROUP] && ParamCase(tags[DEPLOYMENT_GROUP]) === deploymentGroupSlug; } + ({ tags = {} }) => { + return tags[DEPLOYMENT_GROUP] && ParamCase(tags[DEPLOYMENT_GROUP]) === deploymentGroupSlug; + } ) .filter( - ({ state }) => { return NON_IMPORTABLE_STATES.indexOf(state.toUpperCase()) < 0; } + ({ state }) => { + return NON_IMPORTABLE_STATES.indexOf(state.toUpperCase()) < 0; + } ); if (!containers.length) { @@ -1524,7 +2151,9 @@ module.exports = class Data extends EventEmitter { VAsync.forEachParallel({ inputs: service.instances, - func: (instance, next) => { return this.createInstance(instance, next); } + func: (instance, next) => { + return this.createInstance(instance, next); + } }, (err, results) => { if (err) { return cb(err); @@ -1556,7 +2185,13 @@ module.exports = class Data extends EventEmitter { VAsync.forEachParallel({ inputs: Object.keys(services), func: createService(dg.id) - }, (err) => { return cb(err, dg); }); + }, (err) => { + return cb(err, dg); + }); }); } -}; +} + +module.exports = Data; +module.exports.UNKNOWN_INSTANCE_ID = UNKNOWN_INSTANCE_ID; +module.exports.NEW_INSTANCE_ID = NEW_INSTANCE_ID; diff --git a/packages/portal-api/lib/data/transform.js b/packages/portal-api/lib/data/transform.js index 78b9b3fc..097deac4 100644 --- a/packages/portal-api/lib/data/transform.js +++ b/packages/portal-api/lib/data/transform.js @@ -1,7 +1,11 @@ 'use strict'; const Yamljs = require('yamljs'); +const ParamCase = require('param-case'); +const clean = (v) => { + return JSON.parse(JSON.stringify(v)); +}; exports.fromPortal = function ({ portal, datacenter, deploymentGroups, user }) { return { @@ -30,18 +34,31 @@ exports.fromDeploymentGroup = function (deploymentGroup) { slug: deploymentGroup.slug, services: deploymentGroup.services, version: deploymentGroup.version, - history: deploymentGroup.history_version_ids || [] + history: deploymentGroup.history, + imported: deploymentGroup.imported, + status: deploymentGroup.status }; }; -exports.toDeploymentGroup = function ({ name }) { - return { - name, - slug: name, - service_ids: [], - version_id: '', - history_version_ids: [] - }; +exports.toDeploymentGroup = function (clientDeploymentGroup) { + return clean({ + id: clientDeploymentGroup.id, + name: clientDeploymentGroup.name, + slug: clientDeploymentGroup.slug ? + clientDeploymentGroup.slug : + clientDeploymentGroup.name ? + ParamCase(clientDeploymentGroup.name) : + undefined, + service_ids: Array.isArray(clientDeploymentGroup.services) ? clientDeploymentGroup.services.map(({ id }) => { + return id; + }).filter(Boolean) : undefined, + version_id: clientDeploymentGroup.version ? clientDeploymentGroup.version.id : undefined, + history_version_ids: Array.isArray(clientDeploymentGroup.history) ? clientDeploymentGroup.history.map(({ id }) => { + return id; + }).filter(Boolean) : undefined, + imported: clientDeploymentGroup.imported, + status: clientDeploymentGroup.status || 'ACTIVE' + }); }; @@ -58,36 +75,43 @@ exports.fromService = function ({ service, instances, packages }) { connections: service.service_dependency_ids, package: packages ? exports.fromPackage(packages) : {}, parent: service.parent_id || '', - active: service.active + status: service.status, + hasPlan: service.has_plan }; }; exports.toService = function (clientService) { // wat?? - return JSON.parse(JSON.stringify({ + return clean({ id: clientService.id, - version_hash: clientService.hash || clientService.name, + version_hash: clientService.hash, 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 : '', - parent_id: clientService.parent || '', - active: clientService.ative - })); + 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, + status: clientService.status, + has_plan: clientService.hasPlan + }); }; exports.toVersion = function (clientVersion) { - return { + return clean({ id: clientVersion.id, created: clientVersion.created || Date.now(), manifest_id: (clientVersion.manifest || {}).id, - service_scales: clientVersion.scale ? clientVersion.scale.map(exports.toScale) : [], - plan: exports.toPlan(clientVersion.plan || {}) - }; + service_scales: clientVersion.scale ? clientVersion.scale : undefined, + plan: clientVersion.plan ? clientVersion.plan : undefined, + error: clientVersion.version + }); }; exports.fromVersion = function (version) { @@ -95,38 +119,9 @@ exports.fromVersion = function (version) { id: version.id, created: version.created, manifest: version.manifest, - scale: version.service_scales ? version.service_scales.map(exports.fromScale) : [], - plan: exports.fromPlan(version.plan || {}) - }; -}; - - -exports.toScale = function (clientScale) { - return { - service_name: clientScale.serviceName, - replicas: clientScale.replicas - }; -}; - -exports.fromScale = function (scale) { - return { - serviceName: scale.service_name, - replicas: scale.replicas - }; -}; - - -exports.toPlan = function (clientPlan) { - return { - running: clientPlan.running, - actions: clientPlan.actions - }; -}; - -exports.fromPlan = function (plan) { - return { - running: plan.running, - actions: plan.actions + scale: version.service_scales, + plan: version.plan, + error: version.error }; }; @@ -170,21 +165,21 @@ exports.fromInstance = function (instance) { id: instance.id, name: instance.name, machineId: instance.machine_id, - status: instance.status || '', - ips: instance.ips || [], + status: instance.status, + ips: instance.ips, healthy: instance.healthy }; }; exports.toInstance = function (clientInstance) { - return { + return clean({ id: clientInstance.id, name: clientInstance.name, machine_id: clientInstance.machineId, - status: clientInstance.status || '', - ips: clientInstance.ips || [], + status: clientInstance.status, + ips: clientInstance.ips, healthy: clientInstance.healthy - }; + }); }; diff --git a/packages/portal-api/lib/watch/index.js b/packages/portal-api/lib/watch/index.js index faaa43f0..c9540f8d 100644 --- a/packages/portal-api/lib/watch/index.js +++ b/packages/portal-api/lib/watch/index.js @@ -3,20 +3,62 @@ // const Assert = require('assert'); const Throat = require('throat'); const TritonWatch = require('triton-watch'); -const util = require('util'); +const Get = require('lodash.get'); +const Find = require('lodash.find'); +const Util = require('util'); +const ForceArray = require('force-array'); +const VAsync = require('vasync'); const DEPLOYMENT_GROUP = 'docker:label:com.docker.compose.project'; const SERVICE = 'docker:label:com.docker.compose.service'; const HASH = 'docker:label:com.docker.compose.config-hash'; +const ACTION_REMOVE_STATUSES = [ + 'STOPPING', + 'STOPPED', + 'OFFLINE', + 'DELETED', + 'DESTROYED', + 'FAILED', + 'INCOMPLETE', + 'UNKNOWN' +]; + +const ACTION_CREATE_STATUSES = [ + 'READY', + 'ACTIVE', + 'RUNNING', + 'STOPPED', + 'OFFLINE', + 'FAILED', + 'INCOMPLETE', + 'UNKNOWN' +]; + +const SERVICE_STOPPING_STATUSES = [ + 'STOPPED', + 'OFFLINE', + 'FAILED', + 'INCOMPLETE', + 'UNKNOWN' +]; + +const SERVICE_DELETING_STATUSES = [ + 'DELETED', + 'DESTROYED', + 'FAILED', + 'INCOMPLETE', + 'UNKNOWN' +]; + module.exports = class Watcher { constructor (options) { options = options || {}; // todo assert options this._data = options.data; - this._frequency = 500; + this._frequency = 200; this._tritonWatch = new TritonWatch({ frequency: this._frequency, @@ -30,50 +72,68 @@ module.exports = class Watcher { }); this._queues = {}; + this._waitingForPlan = []; + this._isTritonWatchPolling = false; - this._tritonWatch.on('change', (container) => { - return this.onChange(container); + this._tritonWatch.on('change', (machine) => { + return this.onChange(machine); }); - this._tritonWatch.on('all', (containers) => { - containers.forEach((container) => { - this.onChange(container); + this._tritonWatch.on('all', (machines) => { + machines.forEach((machine) => { + this.onChange(machine); }); }); } poll () { - this._tritonWatch.poll(); + if (!this._isTritonWatchPolling) { + this._tritonWatch.poll(); + this._isTritonWatchPolling = true; + } + + if (this._isWaitingPolling) { + return; + } + + this._isWaitingPolling = true; + + setTimeout(() => { + this._isWaitingPolling = false; + this._checkForWaiting(); + }, this._frequency); + } + + _checkForWaiting () { + this._waitingForPlan.forEach(this.onChange); } getContainers () { return this._tritonWatch.getContainers(); } - pushToQueue ({ serviceName, deploymentGroupId }, cb) { - const name = `${deploymentGroupId}-${serviceName}`; - - if (this._queues[name]) { - this._queues[name](cb); + pushToQueue (deploymentGroupId, cb) { + if (this._queues[deploymentGroupId]) { + this._queues[deploymentGroupId](cb); return; } - this._queues[name] = Throat(1); - this._queues[name](cb); + this._queues[deploymentGroupId] = Throat(1); + this._queues[deploymentGroupId](cb); } - getDeploymentGroupId (name, cb) { + getDeploymentGroup (name, cb) { this._data.getDeploymentGroup({ name }, (err, deploymentGroup) => { if (err) { return cb(err); } - return cb(null, deploymentGroup && deploymentGroup.id); + return cb(null, deploymentGroup); }); } - getService ({ serviceName, serviceHash, deploymentGroupId }, cb) { - this._data.getServices({ name: serviceName, hash: serviceHash, deploymentGroupId }, (err, services) => { + getService ({ serviceName, deploymentGroupId }, cb) { + this._data.getServices({ name: serviceName, deploymentGroupId }, (err, services) => { if (err) { return cb(err); } @@ -88,88 +148,395 @@ module.exports = class Watcher { getInstances (service, cb) { service.instances() - .then((instances) => { return cb(null, instances); }) - .catch((err) => { return cb(err); }); + .then((instances) => { + return cb(null, instances); + }) + .catch((err) => { + return cb(err); + }); } - resolveChanges ({ machine, service, instances }, cb) { - // 1. if instance doesn't exist, create new - // 2. if instance exist, update status + getVersion (deploymentGroup, cb) { + deploymentGroup.version() + .then((version) => { + return cb(null, version); + }) + .catch((err) => { + return cb(err); + }); + } - const handleError = (cb) => { - return (err, data) => { - if (err) { - console.error(err); - return; - } + createInstance ({ machine, instances, service }, cb) { + console.error(`-> detected that machine ${machine.name} was created`); - if (cb) { - cb(err, data); - } + const status = (machine.state || '').toUpperCase(); + + if (status === 'DELETED') { + return cb(); + } + + const instance = { + name: machine.name, + status, + ips: machine.ips, + machineId: machine.id + }; + + console.log('-> creating instance', Util.inspect(instance)); + this._data.createInstance(instance, (err, instance) => { + if (err) { + return cb(err); + } + + const payload = { + id: service.id, + instances: instances.concat(instance) }; + + console.log('-> updating service', Util.inspect(payload)); + this._data.updateService(payload, cb); + }); + } + + updateInstance ({ machine, instance, instances, service }, cb) { + console.error(`-> detected that machine ${machine.name} was updated`); + + const updatedInstance = { + id: instance.id, + ips: machine.ips, + status: (machine.state || '').toUpperCase() }; - const isNew = instances - .every(({ machineId }) => { return machine.id !== machineId; }); + console.log('-> updating instance', Util.inspect(updatedInstance)); + this._data.updateInstance(updatedInstance, (err) => { + if (err) { + return cb(err); + } - const instance = instances - .filter(({ machineId }) => { return machine.id === machineId; }) - .pop(); - - const updateService = (updatedService, cb) => { - console.log('-> updating service', util.inspect(updatedService)); - return this._data.updateService(updatedService, handleError(cb)); - }; - - const create = (cb) => { - const status = (machine.state || '').toUpperCase(); - - if (status === 'DELETED') { + if (['DELETED', 'DESTROYED'].indexOf(machine.state.toUpperCase()) < 0) { return cb(); } - const instance = { - name: machine.name, - status, - machineId: machine.id, - ips: machine.ips + + const payload = { + id: service.id, + instances: instances.filter(({ id }) => { + return id !== instance.id; + }) }; - console.log('-> creating instance', util.inspect(instance)); - return this._data.createInstance(instance, handleError((_, instance) => { - return updateService({ - id: service.id, - instances: instances.concat(instance) - }, cb); - })); + console.log('-> updating service', Util.inspect(payload)); + this._data.updateService(payload, cb); + }); + } + + resolveChange ({ deploymentGroup, version, service, instances, machine }, cb) { + console.error(`-> resolving change for machine ${machine.name}`); + + const SERVICE_STATUS = Get(service, 'status', 'UNKNOWN').toUpperCase(); + const MACHINE_STATUS = Get(machine, 'state', 'UNKNOWN').toUpperCase(); + + const hasPlan = Boolean(Get(version, 'plan.hasPlan', true)); + const serviceName = service.name; + + console.error(`-> detected meta for machine ${machine.name} ${Util.inspect({ + SERVICE_STATUS, + MACHINE_STATUS, + hasPlan, + serviceName + })}`); + + const ActionResolvers = { + '_CREATE_OR_REMOVE': (action, cb) => { + console.error(`-> got _CREATE_OR_REMOVE action for "${machine.name}"`); + + let processed = ForceArray(action.processed); + const completed = processed.length === action.toProcess; + + if (completed) { + console.error('-> action was already completed'); + return cb(null, { + action, + completed: true + }); + } + + if (processed.indexOf(machine.id) >= 0) { + console.error('-> machine was already processed'); + return cb(null, { + action, + completed + }); + } + + processed = processed.concat([machine.id]); + + cb(null, { + action: Object.assign({}, action, { + processed + }), + completed: processed.length === action.toProcess + }); + }, + 'NOOP': (action, cb) => { + console.error(`-> got NOOP action for "${machine.name}"`); + + cb(null, { + action, + completed: true + }); + }, + // scenarios: scale down or removed service + // so far, the logic is the same for CREATE and REMOVE + 'REMOVE': (action, cb) => { + console.error(`-> got REMOVE action for "${machine.name}"`); + + if (ACTION_REMOVE_STATUSES.indexOf(MACHINE_STATUS) < 0) { + console.error(`-> since "${machine.name}" is "${MACHINE_STATUS}", nothing to do here`); + + return cb(null, { + action, + completed: false + }); + } + + if (action.machines.indexOf(machine.id) < 0) { + console.error(`-> since "${machine.name}" didn't exist, no need to process its removal`); + return cb(null, { + action, + completed: false + }); + } + + ActionResolvers._CREATE_OR_REMOVE(action, cb); + }, + // scenarios: scale up, recreate, create + // so far, the logic is the same for CREATE and REMOVE + 'CREATE': (action, cb) => { + console.error(`-> got CREATE action for "${machine.name}"`); + + if (ACTION_CREATE_STATUSES.indexOf(MACHINE_STATUS) < 0) { + console.error(`-> since "${machine.name}" is "${MACHINE_STATUS}", nothing to do here`); + + return cb(null, { + action, + completed: false + }); + } + + if (action.machines.indexOf(machine.id) >= 0) { + console.error(`-> since "${machine.name}" already existed, no need to process its creation`); + return cb(null, { + action, + completed: false + }); + } + + ActionResolvers._CREATE_OR_REMOVE(action, cb); + }, + 'START': (action, cb) => { + console.error(`-> got START action for "${machine.name}". redirecting`); + return ActionResolvers.NOOP(action, cb); + } }; - const update = (cb) => { - const updatedInstance = { - id: instance.id, - status: (machine.state || '').toUpperCase() - }; + const toBeActiveServiceResolver = (cb) => { + VAsync.forEachParallel({ + inputs: version.plan, + func: (action, next) => { + if (action.service !== serviceName) { + return next(null, { + action + }); + } - console.log('-> updating instance', util.inspect(updatedInstance)); - return this._data.updateInstance(updatedInstance, handleError(() => { - if (['DELETED', 'DESTROYED'].indexOf(machine.state.toUpperCase()) < 0) { + const ACTION_TYPE = Get(action, 'type', 'NOOP').toUpperCase(); + ActionResolvers[ACTION_TYPE](action, next); + } + }, (err, result) => { + if (err) { + return cb(err); + } + + const newActions = ForceArray(result.successes); + + console.error(`-> got new actions for "${service.name}" ${Util.inspect(newActions)}`); + + const newServiceActions = newActions.filter(({ action }) => { + return action.service === serviceName; + }); + + const isCompleted = newServiceActions.every(({ completed }) => { + return completed; + }); + + console.error(`-> are all actions for "${service.name}" completed? ${isCompleted}`); + + const newPlan = newActions.map(({ action }) => { + return action; + }); + + VAsync.parallel({ + funcs: [ + (cb) => { + console.error(`-> updating Version ${version.id} with new plan ${Util.inspect(newPlan)}`); + + this._data.updateVersion({ + id: version.id, + plan: newPlan + }, cb); + }, + (cb) => { + if (!isCompleted) { + return cb(); + } + + console.error(`-> updating Service ${service.name} with new status: ACTIVE`); + + return this._data.updateService({ + id: service.id, + status: 'ACTIVE' + }, cb); + } + ] + }, cb); + }); + }; + + const ServiceResolvers = { + 'ACTIVE': (cb) => { + console.error(`-> got ACTIVE service "${service.name}". nothing to do`); + + cb(); + }, + 'PROVISIONING': (cb) => { + console.error(`-> got PROVISIONING service "${service.name}"`); + + toBeActiveServiceResolver(cb); + }, + 'SCALING': (cb) => { + console.error(`-> got SCALING service "${service.name}"`); + + toBeActiveServiceResolver(cb); + }, + 'STOPPING': (cb) => { + console.error(`-> got STOPPING service "${service.name}"`); + + if (SERVICE_STOPPING_STATUSES.indexOf(MACHINE_STATUS) < 0) { return cb(); } - return setTimeout(() => { - return updateService({ - id: service.id, - instances: instances.filter(({ id }) => { - return id !== instance.id; - }) - }, cb); - }, this._frequency * 3); - })); + const isComplete = instances + .filter(({ machineId }) => { + return machineId !== machine.id; + }) + .every(({ status }) => { + return SERVICE_STOPPING_STATUSES.indexOf(status) >= 0; + }); + + if (!isComplete) { + return cb(); + } + + this._data.updateService({ + id: service.id, + status: 'STOPPED' + }, cb); + }, + 'STOPPED': (cb) => { + return ServiceResolvers.ACTIVE(cb); + }, + 'DELETING': (cb) => { + console.error(`-> got DELETING service "${service.name}"`); + + if (SERVICE_DELETING_STATUSES.indexOf(MACHINE_STATUS) < 0) { + return cb(); + } + + const isComplete = instances + .filter(({ machineId }) => { + return machineId !== machine.id; + }) + .every(({ status }) => { + return SERVICE_DELETING_STATUSES.indexOf(status) >= 0; + }); + + if (!isComplete) { + return cb(); + } + + VAsync.parallel({ + funcs: [ + (cb) => { + console.error(`-> updating Service ${service.name} to set it DELETED`); + + this._data.updateService({ + id: service.id, + status: 'DELETED' + }, cb); + }, + (cb) => { + console.error(`-> updating DeploymentGroup ${deploymentGroup.id} to remove Service ${service.name}`); + + deploymentGroup.services({}, (err, services) => { + if (err) { + return cb(err); + } + + this._data.updateDeploymentGroup({ + id: deploymentGroup.id, + services: services.filter(({ id }) => { + return service.id !== id; + }) + }, cb); + }); + } + ] + }, cb); + }, + 'DELETED': (cb) => { + return ServiceResolvers.ACTIVE(cb); + }, + 'RESTARTING': (cb) => { + return ServiceResolvers.ACTIVE(cb); + }, + 'UNKNOWN': (cb) => { + return ServiceResolvers.ACTIVE(cb); + } }; - return isNew ? - create(cb) : - update(cb); + const instance = Find(instances, ['machineId', machine.id]); + + const isNew = instances + .every(({ machineId }) => { + return machine.id !== machineId; + }); + + const handleCreateOrUpdatedInstance = (err) => { + if (err) { + return cb(err); + } + + console.error(`-> created/updated machine ${machine.name}`); + + if (!hasPlan) { + console.error(`-> plan for ${service.name} is still not available. queuing`); + this._waitingForPlan.push(machine); + return cb(); + } + + const serviceResolver = ServiceResolvers[SERVICE_STATUS] ? + ServiceResolvers[SERVICE_STATUS] : + ServiceResolvers.UNKNOWN; + + serviceResolver(cb); + }; + + const createOrUpdateInstance = isNew ? + this.createInstance : + this.updateInstance; + + createOrUpdateInstance.call(this, { machine, instances, instance, service }, handleCreateOrUpdatedInstance); } onChange (machine) { @@ -178,7 +545,7 @@ module.exports = class Watcher { return; } - console.log('-> `change` event received', util.inspect(machine)); + console.log('-> `change` event received', Util.inspect(machine)); const { id, tags = {} } = machine; @@ -190,7 +557,9 @@ module.exports = class Watcher { // assert that it's a docker-compose project const isCompose = [DEPLOYMENT_GROUP, SERVICE, HASH].every( - (name) => { return tags[name]; } + (name) => { + return tags[name]; + } ); if (!isCompose) { @@ -201,56 +570,78 @@ module.exports = class Watcher { const deploymentGroupName = tags[DEPLOYMENT_GROUP]; const serviceName = tags[SERVICE]; - const handleError = (next) => { - return (err, item) => { + const getInstancesAndVersion = ({ + service, + deploymentGroup + }, cb) => { + this.getInstances(service, (err, instances) => { if (err) { - console.error(err); - return; + return cb(err); } - next(item); - }; - }; + this.getVersion(deploymentGroup, (err, version) => { + if (err) { + return cb(err); + } - const getInstances = (service, cb) => { - this.getInstances(service, handleError((instances) => { - return this.resolveChanges({ - machine, - service, - instances - }, cb); - })); - }; - - // assert that service exists - const assertService = (deploymentGroupId) => { - this.pushToQueue({ serviceName, deploymentGroupId }, () => { - return new Promise((resolve) => { - this.getService({ serviceName, deploymentGroupId }, handleError((service) => { - if (!service) { - console.error(`Service "${serviceName}" form DeploymentGroup "${deploymentGroupName}" for machine ${id} not found`); - return; - } - - getInstances(service, resolve); - })); + this.resolveChange({ + deploymentGroup, + version, + service, + instances, + machine + }, cb); }); }); }; - // assert that project managed by this portal - const assertDeploymentGroup = () => { - this.getDeploymentGroupId(deploymentGroupName, handleError((deploymentGroupId) => { - if (!deploymentGroupId) { - console.error(`DeploymentGroup "${deploymentGroupName}" for machine ${id} not found`); - return; + // assert that service exists + const assertService = (deploymentGroup, cb) => { + this.getService({ + serviceName, + deploymentGroupId: deploymentGroup.id + }, (err, service) => { + if (err) { + return cb(err); } - assertService(deploymentGroupId); - })); + if (!service) { + console.error(`Service "${serviceName}" form DeploymentGroup "${deploymentGroupName}" for machine ${id} not found`); + return cb(); + } + + getInstancesAndVersion({ + service, + deploymentGroup + }, cb); + }); }; - assertDeploymentGroup(); + // assert that project managed by this portal + // also, lock into `deploymentGroupId` queue + this.getDeploymentGroup(deploymentGroupName, (err, deploymentGroup) => { + if (err) { + console.error(err); + return; + } + + if (!deploymentGroup) { + console.error(`DeploymentGroup "${deploymentGroupName}" for machine ${id} not found`); + return; + } + + this.pushToQueue(deploymentGroup.id, () => { + return new Promise((resolve) => { + assertService(deploymentGroup, (err) => { + if (err) { + console.error(err); + } + + resolve(); + }); + }); + }); + }); } }; diff --git a/packages/portal-api/package.json b/packages/portal-api/package.json index 334cb11f..ed13a82a 100644 --- a/packages/portal-api/package.json +++ b/packages/portal-api/package.json @@ -37,6 +37,9 @@ "graphi": "^2.2.1", "hoek": "^4.1.1", "joyent-cp-gql-schema": "^1.0.4", + "lodash.find": "^4.6.0", + "lodash.flatten": "^4.4.0", + "lodash.get": "^4.4.2", "lodash.uniqby": "^4.7.0", "param-case": "^2.1.1", "penseur": "^7.12.3", diff --git a/packages/portal-api/yarn.lock b/packages/portal-api/yarn.lock deleted file mode 100644 index e5171bbd..00000000 --- a/packages/portal-api/yarn.lock +++ /dev/null @@ -1,2401 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@types/graphql@^0.9.0": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.9.3.tgz#4a2a888e110c796eb7671bf8f9ecef229ad67749" - -JSONStream@0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-0.10.0.tgz#74349d0d89522b71f30f0a03ff9bd20ca6f12ac0" - dependencies: - jsonparse "0.0.5" - through ">=2.2.7 <3" - -accept@2.x.x: - version "2.1.4" - resolved "https://registry.yarnpkg.com/accept/-/accept-2.1.4.tgz#887af54ceee5c7f4430461971ec400c61d09acbb" - dependencies: - boom "5.x.x" - hoek "4.x.x" - -acorn-jsx@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" - dependencies: - acorn "^3.0.4" - -acorn@^3.0.4: - version "3.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" - -acorn@^5.0.1: - version "5.0.3" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d" - -ajv-keywords@^1.0.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" - -ajv@^4.7.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" - dependencies: - co "^4.6.0" - json-stable-stringify "^1.0.1" - -align-text@^0.1.1, align-text@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" - dependencies: - kind-of "^3.0.2" - longest "^1.0.1" - repeat-string "^1.5.2" - -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" - -ammo@2.x.x: - version "2.0.4" - resolved "https://registry.yarnpkg.com/ammo/-/ammo-2.0.4.tgz#bf80aab211698ea78f63ef5e7f113dd5d9e8917f" - dependencies: - boom "5.x.x" - hoek "4.x.x" - -ansi-escapes@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" - -ansi-escapes@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-2.0.0.tgz#5bae52be424878dd9783e8910e3fc2922e83c81b" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - -argparse@^1.0.7: - version "1.0.9" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" - dependencies: - sprintf-js "~1.0.2" - -array-union@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - dependencies: - array-uniq "^1.0.1" - -array-uniq@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - -arrify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - -asn1@~0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" - -assert-plus@0.1.5, assert-plus@0.1.x, assert-plus@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.1.5.tgz#ee74009413002d84cec7219c6ac811812e723160" - -assert-plus@0.2.0, "assert-plus@>=0.2.0 <0.3.0", assert-plus@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - -async@^1.4.0: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - -b64@3.x.x: - version "3.0.2" - resolved "https://registry.yarnpkg.com/b64/-/b64-3.0.2.tgz#7a9d60466adf7b8de114cbdf651a5fdfcc90894d" - -babel-code-frame@^6.16.0, babel-code-frame@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" - dependencies: - chalk "^1.1.0" - esutils "^2.0.2" - js-tokens "^3.0.0" - -backoff@2.4.1, backoff@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.4.1.tgz#2f68c50e0dd789dbefe24200a62efb04d2456d68" - dependencies: - precond "0.2" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - -bcrypt-pbkdf@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" - dependencies: - tweetnacl "^0.14.3" - -belly-button@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/belly-button/-/belly-button-3.1.0.tgz#f7b529978c358e8d4338f8e5fc10e4e3abbafe5d" - dependencies: - bossy "3.x.x" - chalk "1.1.x" - eslint "3.x.x" - glob "7.x.x" - insync "2.x.x" - -bigspinner@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bigspinner/-/bigspinner-3.1.0.tgz#dd3a862b2fedf66fee8471320069428d0d84427a" - -bindings@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" - -bl@^1.0.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.1.tgz#cac328f7bee45730d404b692203fcb590e172d5e" - dependencies: - readable-stream "^2.0.5" - -"bluebird@>= 2.3.2 < 3": - version "2.11.0" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" - -boom@4.x.x, boom@^4.0.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" - dependencies: - hoek "4.x.x" - -boom@5.x.x, boom@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/boom/-/boom-5.1.0.tgz#0308fa8e924cd6d42d9c3bf4883bdc98f0e71df8" - dependencies: - hoek "4.x.x" - -bossy@3.x.x: - version "3.0.4" - resolved "https://registry.yarnpkg.com/bossy/-/bossy-3.0.4.tgz#f9ae9f26e81b41a318f4ee0d83686e4a5c2507b9" - dependencies: - hoek "4.x.x" - joi "10.x.x" - -brace-expansion@^1.1.7: - version "1.1.8" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -brule@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/brule/-/brule-2.0.0.tgz#7020ecb04301f4e1f03f26fc00569edf81258699" - -bunyan@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.5.1.tgz#5f6e7d44c43b952f56b0f41309e3ab12391b4e2d" - optionalDependencies: - dtrace-provider "~0.6" - mv "~2" - safe-json-stringify "~1" - -bunyan@^1.8.3: - version "1.8.10" - resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.10.tgz#201fedd26c7080b632f416072f53a90b9a52981c" - optionalDependencies: - dtrace-provider "~0.8" - moment "^2.10.6" - mv "~2" - safe-json-stringify "~1" - -call-me-maybe@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" - -call@4.x.x: - version "4.0.2" - resolved "https://registry.yarnpkg.com/call/-/call-4.0.2.tgz#df76f5f51ee8dd48b856ac8400f7e69e6d7399c4" - dependencies: - boom "5.x.x" - hoek "4.x.x" - -caller-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" - dependencies: - callsites "^0.2.0" - -callsites@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" - -camelcase@^1.0.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" - -catbox-memory@2.x.x: - version "2.0.4" - resolved "https://registry.yarnpkg.com/catbox-memory/-/catbox-memory-2.0.4.tgz#433e255902caf54233d1286429c8f4df14e822d5" - dependencies: - hoek "4.x.x" - -catbox@7.x.x: - version "7.1.4" - resolved "https://registry.yarnpkg.com/catbox/-/catbox-7.1.4.tgz#8a950ed18b64ba8088c1ae132e85c58479d2b6cc" - dependencies: - boom "5.x.x" - hoek "4.x.x" - joi "10.x.x" - -center-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" - dependencies: - align-text "^0.1.3" - lazy-cache "^1.0.3" - -chalk@1.1.x, chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -circular-json@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" - -cli-cursor@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" - dependencies: - restore-cursor "^1.0.1" - -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - dependencies: - restore-cursor "^2.0.0" - -cli-width@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" - -cliui@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" - dependencies: - center-align "^0.1.1" - right-align "^0.1.1" - wordwrap "0.0.2" - -clone@0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/clone/-/clone-0.1.5.tgz#46f29143d0766d663dbd7f80b7520a15783d2042" - -cmdln@4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/cmdln/-/cmdln-4.1.2.tgz#4345bb5498f2b096ba85ec8c5579a8cb252f7c70" - dependencies: - assert-plus "^1.0.0" - dashdash "^1.14.1" - extsprintf "^1.2.0" - fuzzyset.js "^0.0.1" - verror "^1.6.0" - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - -code@4.1.x, code@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/code/-/code-4.1.0.tgz#209ad11d05af8a0c1c7aaf694d9fa4d2c7d95b85" - dependencies: - hoek "4.x.x" - -commander@^2.7.1: - version "2.10.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.10.0.tgz#e1f5d3245de246d1a5ca04702fa1ad1bd7e405fe" - dependencies: - graceful-readlink ">= 1.0.0" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - -concat-stream@^1.5.2, concat-stream@~1.5.1: - version "1.5.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266" - dependencies: - inherits "~2.0.1" - readable-stream "~2.0.0" - typedarray "~0.0.5" - -concat-stream@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" - dependencies: - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -consulite@1.6.x: - version "1.6.0" - resolved "https://registry.yarnpkg.com/consulite/-/consulite-1.6.0.tgz#d33bb3b736644f4a41fbcf1ae9cce126425c67a8" - dependencies: - or-promise "1.x.x" - wreck "10.x.x" - -consulite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/consulite/-/consulite-2.0.0.tgz#4d35228d24f70a8fb01fdb8ed921d4e54d8bbb16" - dependencies: - or-promise "1.x.x" - wreck "12.x.x" - -content@3.x.x: - version "3.0.4" - resolved "https://registry.yarnpkg.com/content/-/content-3.0.4.tgz#ca3dde04480f12519b71526ec44bd488ddfb3fef" - dependencies: - boom "5.x.x" - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - -cryptiles@3.x.x: - version "3.1.2" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" - dependencies: - boom "5.x.x" - -d@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" - dependencies: - es5-ext "^0.10.9" - -dashdash@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.10.1.tgz#0abf1af89a8f5129a81f18c2b35b21df22622f60" - dependencies: - assert-plus "0.1.x" - -"dashdash@1.x >=1.8", dashdash@^1.12.0, dashdash@^1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - dependencies: - assert-plus "^1.0.0" - -debug@^2.1.1, debug@^2.2.0, debug@^2.6.0, debug@^2.6.8: - version "2.6.8" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" - dependencies: - ms "2.0.0" - -decamelize@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - -deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - -del@^2.0.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" - dependencies: - globby "^5.0.0" - is-path-cwd "^1.0.0" - is-path-in-cwd "^1.0.0" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - rimraf "^2.2.8" - -diff@3.x.x: - version "3.2.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" - -docker-compose-client@^1.0.8: - version "1.0.9" - resolved "https://registry.yarnpkg.com/docker-compose-client/-/docker-compose-client-1.0.9.tgz#f92daddad019fcc25014d67c98e33b06ed5917ed" - dependencies: - zerorpc "^0.9.7" - -docker-modem@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-1.0.0.tgz#cd7f43e209d7a4cff28a2628bae6381414bd8b8b" - dependencies: - JSONStream "0.10.0" - debug "^2.6.0" - readable-stream "~1.0.26-4" - split-ca "^1.0.0" - -dockerode@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-2.5.0.tgz#3fb1ff0e14b6901d92abff75a8a5440569f0538b" - dependencies: - concat-stream "~1.5.1" - docker-modem "^1.0.0" - tar-fs "~1.12.0" - -doctrine@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63" - dependencies: - esutils "^2.0.2" - isarray "^1.0.0" - -dtrace-provider@^0.7.0: - version "0.7.1" - resolved "https://registry.yarnpkg.com/dtrace-provider/-/dtrace-provider-0.7.1.tgz#c06b308f2f10d5d5838aec9c571e5d588dc71d04" - dependencies: - nan "^2.3.3" - -dtrace-provider@~0.6: - version "0.6.0" - resolved "https://registry.yarnpkg.com/dtrace-provider/-/dtrace-provider-0.6.0.tgz#0b078d5517937d873101452d9146737557b75e51" - dependencies: - nan "^2.0.8" - -dtrace-provider@~0.8: - version "0.8.3" - resolved "https://registry.yarnpkg.com/dtrace-provider/-/dtrace-provider-0.8.3.tgz#ba1bfc6493285ccfcfc6ab69cd5c61d74c2a43bf" - dependencies: - nan "^2.3.3" - -duplexify@^3.1.2: - version "3.5.0" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.0.tgz#1aa773002e1578457e9d9d4a50b0ccaaebcbd604" - dependencies: - end-of-stream "1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" - -ecc-jsbn@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" - dependencies: - jsbn "~0.1.0" - -end-of-stream@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.0.0.tgz#d4596e702734a93e40e9af864319eabd99ff2f0e" - dependencies: - once "~1.3.0" - -end-of-stream@^1.0.0, end-of-stream@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.0.tgz#7a90d833efda6cfa6eac0f4949dbb0fad3a63206" - dependencies: - once "^1.4.0" - -es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: - version "0.10.23" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.23.tgz#7578b51be974207a5487821b56538c224e4e7b38" - dependencies: - es6-iterator "2" - es6-symbol "~3.1" - -es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512" - dependencies: - d "1" - es5-ext "^0.10.14" - es6-symbol "^3.1" - -es6-map@^0.1.3: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-set "~0.1.5" - es6-symbol "~3.1.1" - event-emitter "~0.3.5" - -es6-promise@^3.0.2, es6-promise@^3.1.2: - version "3.3.1" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" - -es6-set@~0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-symbol "3.1.1" - event-emitter "~0.3.5" - -es6-symbol@3.1.1, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" - dependencies: - d "1" - es5-ext "~0.10.14" - -es6-weak-map@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" - dependencies: - d "1" - es5-ext "^0.10.14" - es6-iterator "^2.0.1" - es6-symbol "^3.1.1" - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - -escope@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" - dependencies: - es6-map "^0.1.3" - es6-weak-map "^2.0.1" - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-config-hapi@10.x.x: - version "10.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-hapi/-/eslint-config-hapi-10.0.0.tgz#9980affd76103ebc1fec92b45638345db19348f5" - -eslint-plugin-hapi@4.x.x: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-hapi/-/eslint-plugin-hapi-4.0.0.tgz#44aa2e45f7939a523929cd832bb9aa129a95e823" - dependencies: - hapi-capitalize-modules "1.x.x" - hapi-for-you "1.x.x" - hapi-scope-start "2.x.x" - no-arrowception "1.x.x" - -eslint-scope@^3.7.1: - version "3.7.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint@3.x.x: - version "3.19.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc" - dependencies: - babel-code-frame "^6.16.0" - chalk "^1.1.3" - concat-stream "^1.5.2" - debug "^2.1.1" - doctrine "^2.0.0" - escope "^3.6.0" - espree "^3.4.0" - esquery "^1.0.0" - estraverse "^4.2.0" - esutils "^2.0.2" - file-entry-cache "^2.0.0" - glob "^7.0.3" - globals "^9.14.0" - ignore "^3.2.0" - imurmurhash "^0.1.4" - inquirer "^0.12.0" - is-my-json-valid "^2.10.0" - is-resolvable "^1.0.0" - js-yaml "^3.5.1" - json-stable-stringify "^1.0.0" - levn "^0.3.0" - lodash "^4.0.0" - mkdirp "^0.5.0" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.1" - pluralize "^1.2.1" - progress "^1.1.8" - require-uncached "^1.0.2" - shelljs "^0.7.5" - strip-bom "^3.0.0" - strip-json-comments "~2.0.1" - table "^3.7.8" - text-table "~0.2.0" - user-home "^2.0.0" - -eslint@4.0.x: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.0.0.tgz#7277c01437fdf41dccd168d5aa0e49b75ca1f260" - dependencies: - babel-code-frame "^6.22.0" - chalk "^1.1.3" - concat-stream "^1.6.0" - debug "^2.6.8" - doctrine "^2.0.0" - eslint-scope "^3.7.1" - espree "^3.4.3" - esquery "^1.0.0" - estraverse "^4.2.0" - esutils "^2.0.2" - file-entry-cache "^2.0.0" - glob "^7.1.2" - globals "^9.17.0" - ignore "^3.3.3" - imurmurhash "^0.1.4" - inquirer "^3.0.6" - is-my-json-valid "^2.16.0" - is-resolvable "^1.0.0" - js-yaml "^3.8.4" - json-stable-stringify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.4" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.2" - pluralize "^4.0.0" - progress "^2.0.0" - require-uncached "^1.0.3" - strip-json-comments "~2.0.1" - table "^4.0.1" - text-table "~0.2.0" - -espree@3.4.x, espree@^3.4.0, espree@^3.4.3: - version "3.4.3" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.3.tgz#2910b5ccd49ce893c2ffffaab4fd8b3a31b82374" - dependencies: - acorn "^5.0.1" - acorn-jsx "^3.0.0" - -esprima@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" - -esquery@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa" - dependencies: - estraverse "^4.0.0" - -esrecurse@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" - dependencies: - estraverse "^4.1.0" - object-assign "^4.0.1" - -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" - -esutils@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" - -event-emitter@~0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - dependencies: - d "1" - es5-ext "~0.10.14" - -exit-hook@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" - -external-editor@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.0.4.tgz#1ed9199da9cbfe2ef2f7a31b2fde8b0d12368972" - dependencies: - iconv-lite "^0.4.17" - jschardet "^1.4.2" - tmp "^0.0.31" - -extsprintf@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.0.tgz#4d58b815ace5bebfc4ebf03cf98b0a7604a99b86" - -extsprintf@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" - -extsprintf@1.2.0, extsprintf@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.2.0.tgz#5ad946c22f5b32ba7f8cd7426711c6e8a3fc2529" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - -fast-levenshtein@~2.0.4: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - -fast-safe-stringify@1.1.x, fast-safe-stringify@^1.1.3: - version "1.1.13" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-1.1.13.tgz#a01e9cd9c9e491715c98a75a42d5f0bbd107ff76" - -figures@^1.3.5: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" - -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" - dependencies: - flat-cache "^1.2.1" - object-assign "^4.0.1" - -find-rc@3.0.x: - version "3.0.1" - resolved "https://registry.yarnpkg.com/find-rc/-/find-rc-3.0.1.tgz#54a4178370f10bc9371fa8d1b2c2809a2afa0cce" - -flat-cache@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" - dependencies: - circular-json "^0.3.1" - del "^2.0.2" - graceful-fs "^4.1.2" - write "^0.2.1" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - -fuzzyset.js@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/fuzzyset.js/-/fuzzyset.js-0.0.1.tgz#979e22f9451b4b38f051f7937c919dbacc692958" - -generate-function@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" - -generate-object-property@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" - dependencies: - is-property "^1.0.0" - -getpass@0.1.6, getpass@^0.1.1: - version "0.1.6" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" - dependencies: - assert-plus "^1.0.0" - -glob@7.x.x, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^5.0.14: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - -globals@^9.14.0, globals@^9.17.0: - version "9.18.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" - -globby@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" - dependencies: - array-union "^1.0.1" - arrify "^1.0.0" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -good-console@^6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/good-console/-/good-console-6.4.0.tgz#7294c9d90c4c9f059a082e180625495966d2ba59" - dependencies: - hoek "4.x.x" - joi "8.1.x" - json-stringify-safe "5.0.x" - moment "2.15.x" - -good-squeeze@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/good-squeeze/-/good-squeeze-5.0.2.tgz#a8e58242b4a0b32cdbdf317b60e73a19a7f0879b" - dependencies: - fast-safe-stringify "1.1.x" - hoek "4.x.x" - -good@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/good/-/good-7.2.0.tgz#c94daa44bfb96388f28f6ce8ac8bfd468c2df704" - dependencies: - hoek "4.x.x" - joi "10.x.x" - oppsy "1.x.x" - pumpify "1.3.x" - -graceful-fs@^4.1.2: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" - -graphi@^2.2.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/graphi/-/graphi-2.3.0.tgz#e6036b6b2e8024451311ef63fd937896fb1dfeb2" - dependencies: - boom "5.x.x" - graphql "0.10.x" - graphql-server-core "0.9.x" - graphql-server-module-graphiql "0.9.x" - -graphql-server-core@0.9.x: - version "0.9.0" - resolved "https://registry.yarnpkg.com/graphql-server-core/-/graphql-server-core-0.9.0.tgz#34dac2d90e0b9c5f2c4c666f08dab74c15b8a327" - optionalDependencies: - "@types/graphql" "^0.9.0" - -graphql-server-module-graphiql@0.9.x: - version "0.9.0" - resolved "https://registry.yarnpkg.com/graphql-server-module-graphiql/-/graphql-server-module-graphiql-0.9.0.tgz#dfe877066052f94a3ff57ff4790b3023e9ef2b4a" - -graphql@0.10.x: - version "0.10.3" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.10.3.tgz#c313afd5518e673351bee18fb63e2a0e487407ab" - dependencies: - iterall "^1.1.0" - -handlebars@4.x.x, handlebars@^4.0.5: - version "4.0.10" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.10.tgz#3d30c718b09a3d96f23ea4cc1f403c4d3ba9ff4f" - dependencies: - async "^1.4.0" - optimist "^0.6.1" - source-map "^0.4.4" - optionalDependencies: - uglify-js "^2.6" - -hapi-capitalize-modules@1.x.x: - version "1.1.6" - resolved "https://registry.yarnpkg.com/hapi-capitalize-modules/-/hapi-capitalize-modules-1.1.6.tgz#7991171415e15e6aa3231e64dda73c8146665318" - -hapi-for-you@1.x.x: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hapi-for-you/-/hapi-for-you-1.0.0.tgz#d362fbee8d7bda9c2c7801e207e5a5cd1a0b6a7b" - -hapi-scope-start@2.x.x: - version "2.1.1" - resolved "https://registry.yarnpkg.com/hapi-scope-start/-/hapi-scope-start-2.1.1.tgz#7495a726fe72b7bca8de2cdcc1d87cd8ce6ab4f2" - -hapi-swagger@^7.7.0: - version "7.7.0" - resolved "https://registry.yarnpkg.com/hapi-swagger/-/hapi-swagger-7.7.0.tgz#86b79ff50a0900110a9e83595941a9edbc7b6ad6" - dependencies: - boom "^4.0.0" - handlebars "^4.0.5" - hoek "^4.0.1" - http-status "^1.0.1" - joi "10.2.2" - json-schema-ref-parser "^3.1.2" - swagger-parser "^3.4.1" - -hapi@^16.4.3: - version "16.4.3" - resolved "https://registry.yarnpkg.com/hapi/-/hapi-16.4.3.tgz#be4daaf2dcdbee97957ce503061b09765078aa05" - dependencies: - accept "2.x.x" - ammo "2.x.x" - boom "5.x.x" - call "4.x.x" - catbox "7.x.x" - catbox-memory "2.x.x" - cryptiles "3.x.x" - heavy "4.x.x" - hoek "4.x.x" - iron "4.x.x" - items "2.x.x" - joi "10.x.x" - mimos "3.x.x" - podium "1.x.x" - shot "3.x.x" - statehood "5.x.x" - subtext "4.x.x" - topo "2.x.x" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - dependencies: - ansi-regex "^2.0.0" - -heavy@4.x.x: - version "4.0.4" - resolved "https://registry.yarnpkg.com/heavy/-/heavy-4.0.4.tgz#36c91336c00ccfe852caa4d153086335cd2f00e9" - dependencies: - boom "5.x.x" - hoek "4.x.x" - joi "10.x.x" - -hoek@4.x.x, hoek@^4.0.1, hoek@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.1.1.tgz#9cc573ffba2b7b408fb5e9c2a13796be94cddce9" - -http-signature@^1.0.2: - version "1.1.1" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" - dependencies: - assert-plus "^0.2.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -http-status@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/http-status/-/http-status-1.0.1.tgz#dc43001a8bfc50ac87d485a892f7578964bc94a2" - -iconv-lite@^0.4.17: - version "0.4.18" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2" - -ignore@^3.2.0, ignore@^3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - -inert@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/inert/-/inert-4.2.0.tgz#6aa5da8ce19982eb72aabef4bdda64fd6750b005" - dependencies: - ammo "2.x.x" - boom "4.x.x" - hoek "4.x.x" - items "2.x.x" - joi "10.x.x" - lru-cache "4.0.x" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - -inquirer@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" - dependencies: - ansi-escapes "^1.1.0" - ansi-regex "^2.0.0" - chalk "^1.0.0" - cli-cursor "^1.0.1" - cli-width "^2.0.0" - figures "^1.3.5" - lodash "^4.3.0" - readline2 "^1.0.1" - run-async "^0.1.0" - rx-lite "^3.1.2" - string-width "^1.0.1" - strip-ansi "^3.0.0" - through "^2.3.6" - -inquirer@^3.0.6: - version "3.1.1" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.1.1.tgz#87621c4fba4072f48a8dd71c9f9df6f100b2d534" - dependencies: - ansi-escapes "^2.0.0" - chalk "^1.0.0" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^2.0.4" - figures "^2.0.0" - lodash "^4.3.0" - mute-stream "0.0.7" - run-async "^2.2.0" - rx-lite "^4.0.8" - rx-lite-aggregates "^4.0.8" - string-width "^2.0.0" - strip-ansi "^3.0.0" - through "^2.3.6" - -insync@2.x.x: - version "2.1.1" - resolved "https://registry.yarnpkg.com/insync/-/insync-2.1.1.tgz#22e26c61121303c06f51d35a3ccf6d8fc1e914c4" - -interpret@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90" - -iron@4.x.x: - version "4.0.5" - resolved "https://registry.yarnpkg.com/iron/-/iron-4.0.5.tgz#4f042cceb8b9738f346b59aa734c83a89bc31428" - dependencies: - boom "5.x.x" - cryptiles "3.x.x" - hoek "4.x.x" - -is-absolute@^0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.1.7.tgz#847491119fccb5fb436217cc737f7faad50f603f" - dependencies: - is-relative "^0.1.0" - -is-buffer@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - -is-my-json-valid@^2.10.0, is-my-json-valid@^2.16.0: - version "2.16.0" - resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693" - dependencies: - generate-function "^2.0.0" - generate-object-property "^1.1.0" - jsonpointer "^4.0.0" - xtend "^4.0.0" - -is-path-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" - -is-path-in-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" - dependencies: - is-path-inside "^1.0.0" - -is-path-inside@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" - dependencies: - path-is-inside "^1.0.1" - -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - -is-property@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" - -is-relative@^0.1.0: - version "0.1.3" - resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.1.3.tgz#905fee8ae86f45b3ec614bc3c15c869df0876e82" - -is-resolvable@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" - dependencies: - tryit "^1.0.1" - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - -isarray@^1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - -isemail@2.x.x: - version "2.2.1" - resolved "https://registry.yarnpkg.com/isemail/-/isemail-2.2.1.tgz#0353d3d9a62951080c262c2aa0a42b8ea8e9e2a6" - -isexe@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" - -items@2.x.x: - version "2.1.1" - resolved "https://registry.yarnpkg.com/items/-/items-2.1.1.tgz#8bd16d9c83b19529de5aea321acaada78364a198" - -iterall@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.1.1.tgz#f7f0af11e9a04ec6426260f5019d9fcca4d50214" - -jodid25519@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" - dependencies: - jsbn "~0.1.0" - -joi@10.2.2: - version "10.2.2" - resolved "https://registry.yarnpkg.com/joi/-/joi-10.2.2.tgz#dc5a792b7b4c6fffa562242a95b55d9d3f077e24" - dependencies: - hoek "4.x.x" - isemail "2.x.x" - items "2.x.x" - topo "2.x.x" - -joi@10.x.x: - version "10.6.0" - resolved "https://registry.yarnpkg.com/joi/-/joi-10.6.0.tgz#52587f02d52b8b75cdb0c74f0b164a191a0e1fc2" - dependencies: - hoek "4.x.x" - isemail "2.x.x" - items "2.x.x" - topo "2.x.x" - -joi@8.1.x: - version "8.1.1" - resolved "https://registry.yarnpkg.com/joi/-/joi-8.1.1.tgz#2d8b52a5d909d217ed47248577eefe8b1798f48f" - dependencies: - hoek "4.x.x" - isemail "2.x.x" - moment "2.x.x" - topo "2.x.x" - -joyent-cp-gql-schema@^1.0.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/joyent-cp-gql-schema/-/joyent-cp-gql-schema-1.2.0.tgz#da86bc80fa54406c81107be3519c804ecdf69143" - -js-tokens@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" - -js-yaml@^3.4.6, js-yaml@^3.5.1, js-yaml@^3.6.0, js-yaml@^3.8.4: - version "3.8.4" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6" - dependencies: - argparse "^1.0.7" - esprima "^3.1.1" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - -jschardet@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.4.2.tgz#2aa107f142af4121d145659d44f50830961e699a" - -json-schema-ref-parser@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/json-schema-ref-parser/-/json-schema-ref-parser-1.4.1.tgz#c0c2e438bf0796723b02451bae8bc7dd0b37fed0" - dependencies: - call-me-maybe "^1.0.1" - debug "^2.2.0" - es6-promise "^3.0.2" - js-yaml "^3.4.6" - ono "^2.0.1" - -json-schema-ref-parser@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/json-schema-ref-parser/-/json-schema-ref-parser-3.1.2.tgz#a38ecb7774f87f32e7eb9723d5921390e76a9a42" - dependencies: - call-me-maybe "^1.0.1" - debug "^2.2.0" - es6-promise "^3.1.2" - js-yaml "^3.6.0" - ono "^2.2.1" - z-schema "^3.17.0" - -json-schema@0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.2.tgz#50354f19f603917c695f70b85afa77c3b0f23506" - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - -json-stable-stringify@1.x.x, json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - dependencies: - jsonify "~0.0.0" - -json-stringify-safe@5.0.x, json-stringify-safe@5.x.x: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - -json5@0.5.x: - version "0.5.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" - -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - -jsonparse@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-0.0.5.tgz#330542ad3f0a654665b778f3eb2d9a9fa507ac64" - -jsonpointer@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" - -jsprim@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-0.3.0.tgz#cd13466ea2480dbd8396a570d47d31dda476f8b1" - dependencies: - extsprintf "1.0.0" - json-schema "0.2.2" - verror "1.3.3" - -jsprim@^1.2.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918" - dependencies: - assert-plus "1.0.0" - extsprintf "1.0.2" - json-schema "0.2.3" - verror "1.3.6" - -keep-alive-agent@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/keep-alive-agent/-/keep-alive-agent-0.0.1.tgz#44847ca394ce8d6b521ae85816bd64509942b385" - -kind-of@^3.0.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - dependencies: - is-buffer "^1.1.5" - -lab@^14.0.1: - version "14.1.0" - resolved "https://registry.yarnpkg.com/lab/-/lab-14.1.0.tgz#2632b9a416d5f391d9a6f1d98d607b0d69f75629" - dependencies: - bossy "3.x.x" - code "4.1.x" - diff "3.x.x" - eslint "4.0.x" - eslint-config-hapi "10.x.x" - eslint-plugin-hapi "4.x.x" - espree "3.4.x" - find-rc "3.0.x" - handlebars "4.x.x" - hoek "4.x.x" - items "2.x.x" - json-stable-stringify "1.x.x" - json-stringify-safe "5.x.x" - mkdirp "0.5.x" - seedrandom "2.4.x" - source-map "0.5.x" - source-map-support "0.4.x" - -lazy-cache@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" - -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -lodash.get@^4.1.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" - -lodash.isequal@^4.4.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - -lodash.uniqby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302" - -lodash@^3.10.1, lodash@^3.9.3: - version "3.10.1" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" - -lodash@^4.0.0, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.7.0: - version "4.17.4" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" - -lomstream@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/lomstream/-/lomstream-1.1.0.tgz#2a7f8066ec3ab40bef28ca384842e75340183bf0" - dependencies: - assert-plus "0.1.5" - extsprintf "1.3.0" - vstream "0.1.0" - -longest@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - -lower-case@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" - -lru-cache@4.0.x, lru-cache@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" - dependencies: - pseudomap "^1.0.1" - yallist "^2.0.0" - -lstream@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/lstream/-/lstream-0.0.4.tgz#d637764ea33a929bd00f34d2a23c2256d0d5fb5b" - dependencies: - readable-stream ">= 1.0.2" - -mime-db@1.x.x: - version "1.28.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.28.0.tgz#fedd349be06d2865b7fc57d837c6de4f17d7ac3c" - -mime@^1.3.4: - version "1.3.6" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0" - -mimic-fn@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" - -mimos@3.x.x: - version "3.0.3" - resolved "https://registry.yarnpkg.com/mimos/-/mimos-3.0.3.tgz#b9109072ad378c2b72f6a0101c43ddfb2b36641f" - dependencies: - hoek "4.x.x" - mime-db "1.x.x" - -"minimatch@2 || 3", minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8, minimist@~0.0.1: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - -mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - dependencies: - minimist "0.0.8" - -moment@2.15.x, moment@2.x.x, moment@^2.10.6: - version "2.15.2" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.15.2.tgz#1bfdedf6a6e345f322fe956d5df5bd08a8ce84dc" - -mooremachine@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mooremachine/-/mooremachine-2.1.0.tgz#e935cf356ca6b6a28b92fbd446d1b31a5c19848d" - dependencies: - assert-plus ">=0.2.0 <0.3.0" - optionalDependencies: - dtrace-provider "~0.8" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - -msgpack@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/msgpack/-/msgpack-1.0.2.tgz#923e2c5cffa65c8418e9b228d1124793969c429c" - dependencies: - nan "^2.0.9" - -mute-stream@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" - -mute-stream@0.0.7, mute-stream@~0.0.4: - version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - -mv@~2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2" - dependencies: - mkdirp "~0.5.1" - ncp "~2.0.0" - rimraf "~2.4.0" - -nan@^2.0.8, nan@^2.0.9, nan@^2.3.3: - version "2.6.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" - -nan@~2.3.0: - version "2.3.5" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.3.5.tgz#822a0dc266290ce4cd3a12282ca3e7e364668a08" - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - -ncp@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" - -nigel@2.x.x: - version "2.0.2" - resolved "https://registry.yarnpkg.com/nigel/-/nigel-2.0.2.tgz#93a1866fb0c52d87390aa75e2b161f4b5c75e5b1" - dependencies: - hoek "4.x.x" - vise "2.x.x" - -no-arrowception@1.x.x: - version "1.0.0" - resolved "https://registry.yarnpkg.com/no-arrowception/-/no-arrowception-1.0.0.tgz#5bf3e95eb9c41b57384a805333daa3b734ee327a" - -no-case@^2.2.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.1.tgz#7aeba1c73a52184265554b7dc03baf720df80081" - dependencies: - lower-case "^1.1.1" - -node-uuid@^1.4.6: - version "1.4.8" - resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - -object-assign@^4.0.1, object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - -once@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.3.0.tgz#151af86bfc1f08c4b9f07d06ab250ffcbeb56581" - -once@1.3.2, once@^1.3.0, once@^1.3.1, once@^1.3.2, once@~1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/once/-/once-1.3.2.tgz#d8feeca93b039ec1dcdee7741c92bdac5e28081b" - dependencies: - wrappy "1" - -once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - dependencies: - wrappy "1" - -onetime@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" - -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - dependencies: - mimic-fn "^1.0.0" - -ono@^2.0.1, ono@^2.2.1: - version "2.2.5" - resolved "https://registry.yarnpkg.com/ono/-/ono-2.2.5.tgz#daf09488b51174da7a7e4275dfab31b438ffa0e3" - -oppsy@1.x.x, oppsy@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/oppsy/-/oppsy-1.0.2.tgz#98014cd6967653a83cfffa554226dc90050baad4" - dependencies: - hoek "4.x.x" - items "2.x.x" - -optimist@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" - dependencies: - minimist "~0.0.1" - wordwrap "~0.0.2" - -optionator@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.4" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - wordwrap "~1.0.0" - -or-promise@1.x.x: - version "1.0.0" - resolved "https://registry.yarnpkg.com/or-promise/-/or-promise-1.0.0.tgz#158263c37ecf68c17a3dc669dcdb6a4bce21c85c" - -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - -os-tmpdir@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - -param-case@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" - dependencies: - no-case "^2.2.0" - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - -path-is-inside@^1.0.1, path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - -path-parse@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" - -penseur@^7.12.3: - version "7.12.3" - resolved "https://registry.yarnpkg.com/penseur/-/penseur-7.12.3.tgz#07032c9f206fe3188934c3c36c69b0f04e6ab20b" - dependencies: - boom "5.x.x" - hoek "4.x.x" - items "2.x.x" - joi "10.x.x" - radix62 "1.x.x" - rethinkdb "2.3.x" - -pez@2.x.x: - version "2.1.5" - resolved "https://registry.yarnpkg.com/pez/-/pez-2.1.5.tgz#5ec2cc62500cc3eb4236d4a414cf5a17b5eb5007" - dependencies: - b64 "3.x.x" - boom "5.x.x" - content "3.x.x" - hoek "4.x.x" - nigel "2.x.x" - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - -piloted@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/piloted/-/piloted-3.1.1.tgz#3eabeaac9bb02763e70da26caef634099b2a1fc9" - dependencies: - consulite "1.6.x" - hoek "4.x.x" - items "2.x.x" - json5 "0.5.x" - or-promise "1.x.x" - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - -pluralize@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" - -pluralize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-4.0.0.tgz#59b708c1c0190a2f692f1c7618c446b052fd1762" - -podium@1.x.x: - version "1.2.5" - resolved "https://registry.yarnpkg.com/podium/-/podium-1.2.5.tgz#87c566c2f0365bcf0a1ec7602c4d01948cdd2ad5" - dependencies: - hoek "4.x.x" - items "2.x.x" - joi "10.x.x" - -precond@0.2: - version "0.2.3" - resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - -process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - -progress@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" - -progress@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" - -pseudomap@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - -pump@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.2.tgz#3b3ee6512f94f0e575538c17995f9f16990a5d51" - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pumpify@1.3.x: - version "1.3.5" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.3.5.tgz#1b671c619940abcaeac0ad0e3a3c164be760993b" - dependencies: - duplexify "^3.1.2" - inherits "^2.0.1" - pump "^1.0.0" - -putmetric@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/putmetric/-/putmetric-1.0.1.tgz#5b0362732545b4e05c91ef5e087fae075d638a12" - -radix62@1.x.x: - version "1.0.1" - resolved "https://registry.yarnpkg.com/radix62/-/radix62-1.0.1.tgz#cc2f27a49543b44ddaac712380409354bbe009a5" - -read@1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" - dependencies: - mute-stream "~0.0.4" - -"readable-stream@>= 1.0.2", readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.1.4, readable-stream@^2.2.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - safe-buffer "~5.1.1" - string_decoder "~1.0.3" - util-deprecate "~1.0.1" - -readable-stream@~1.0.26-4: - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@~2.0.0: - version "2.0.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" - -readline2@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - mute-stream "0.0.5" - -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - dependencies: - resolve "^1.1.6" - -repeat-string@^1.5.2: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - -require-uncached@^1.0.2, require-uncached@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" - dependencies: - caller-path "^0.1.0" - resolve-from "^1.0.0" - -resolve-from@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" - -resolve@^1.1.6: - version "1.3.3" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5" - dependencies: - path-parse "^1.0.5" - -restify-clients@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/restify-clients/-/restify-clients-1.5.0.tgz#7a417f8b20782958e3f27b5cc7651bd47631fa37" - dependencies: - assert-plus "^1.0.0" - backoff "^2.4.1" - bunyan "^1.8.3" - dtrace-provider "^0.7.0" - fast-safe-stringify "^1.1.3" - keep-alive-agent "0.0.1" - lodash "^4.7.0" - lru-cache "^4.0.1" - mime "^1.3.4" - node-uuid "^1.4.6" - once "^1.3.2" - restify-errors "^3.1.0" - semver "^5.0.1" - tunnel-agent "^0.4.0" - -restify-errors@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/restify-errors/-/restify-errors-3.0.0.tgz#3b17177d43954acece4291465a97ce1b58cf3d57" - dependencies: - assert-plus "^0.1.5" - lodash "^3.9.3" - verror "^1.6.0" - -restify-errors@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restify-errors/-/restify-errors-3.1.0.tgz#06b5479477874c0856d782a12c8707dcdad53f16" - dependencies: - assert-plus "^0.2.0" - lodash "^3.10.1" - verror "^1.6.0" - -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" - dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" - -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" - -rethinkdb@2.3.x: - version "2.3.3" - resolved "https://registry.yarnpkg.com/rethinkdb/-/rethinkdb-2.3.3.tgz#3dc6586e22fa1dabee0d254e64bd0e379fad2f72" - dependencies: - bluebird ">= 2.3.2 < 3" - -right-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" - dependencies: - align-text "^0.1.1" - -rimraf@2.4.4, rimraf@^2.2.8, rimraf@~2.4.0: - version "2.4.4" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.4.tgz#b528ce2ebe0e6d89fb03b265de11d61da0dbcf82" - dependencies: - glob "^5.0.14" - -run-async@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" - dependencies: - once "^1.3.0" - -run-async@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" - dependencies: - is-promise "^2.1.0" - -rx-lite-aggregates@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" - dependencies: - rx-lite "*" - -rx-lite@*, rx-lite@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" - -rx-lite@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" - -safe-json-stringify@~1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz#81a098f447e4bbc3ff3312a243521bc060ef5911" - -seedrandom@2.4.x: - version "2.4.3" - resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-2.4.3.tgz#2438504dad33917314bff18ac4d794f16d6aaecc" - -semver@5.1.0, semver@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.1.0.tgz#85f2cf8550465c4df000cf7d86f6b054106ab9e5" - -shelljs@^0.7.5: - version "0.7.8" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" - dependencies: - glob "^7.0.0" - interpret "^1.0.0" - rechoir "^0.6.2" - -shot@3.x.x: - version "3.4.2" - resolved "https://registry.yarnpkg.com/shot/-/shot-3.4.2.tgz#1e5c3f6f2b26649adc42f7eb350214a5a0291d67" - dependencies: - hoek "4.x.x" - joi "10.x.x" - -signal-exit@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" - -smartdc-auth@2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/smartdc-auth/-/smartdc-auth-2.5.2.tgz#74f3511958b6e5ac4f1c558992da6049e1dfc035" - dependencies: - assert-plus "^1.0.0" - bunyan "1.5.1" - clone "0.1.5" - dashdash "1.10.1" - http-signature "^1.0.2" - once "1.3.0" - sshpk "^1.8.3" - sshpk-agent "^1.3.0" - vasync "1.4.3" - -source-map-support@0.4.x: - version "0.4.15" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1" - dependencies: - source-map "^0.5.6" - -source-map@0.5.x, source-map@^0.5.6, source-map@~0.5.1: - version "0.5.6" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" - -source-map@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" - dependencies: - amdefine ">=0.0.4" - -split-ca@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - -sshpk-agent@1.4.2, sshpk-agent@^1.3.0: - version "1.4.2" - resolved "https://registry.yarnpkg.com/sshpk-agent/-/sshpk-agent-1.4.2.tgz#5739cc08f48f98c53950a1715d20872a53804541" - dependencies: - assert-plus "^1.0.0" - mooremachine "^2.0.1" - readable-stream "^2.1.4" - sshpk ">=1.9.1 < 1.11.0" - -sshpk@1.10.2, "sshpk@>=1.9.1 < 1.11.0", sshpk@^1.7.0, sshpk@^1.8.3: - version "1.10.2" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.10.2.tgz#d5a804ce22695515638e798dbe23273de070a5fa" - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - dashdash "^1.12.0" - getpass "^0.1.1" - optionalDependencies: - bcrypt-pbkdf "^1.0.0" - ecc-jsbn "~0.1.1" - jodid25519 "^1.0.0" - jsbn "~0.1.0" - tweetnacl "~0.14.0" - -statehood@5.x.x: - version "5.0.2" - resolved "https://registry.yarnpkg.com/statehood/-/statehood-5.0.2.tgz#c6b3baa16ed8b121d3f09a3ffa85e22195a7f2a9" - dependencies: - boom "5.x.x" - cryptiles "3.x.x" - hoek "4.x.x" - iron "4.x.x" - items "2.x.x" - joi "10.x.x" - -stream-shift@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" - -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string-width@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.0.tgz#030664561fc146c9423ec7d978fe2457437fe6d0" - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - -string_decoder@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - dependencies: - ansi-regex "^3.0.0" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - -strsplit@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strsplit/-/strsplit-1.0.0.tgz#0fdedc68e91addcfcb2e6be9c262581a6e8c28aa" - -subtext@4.x.x: - version "4.4.1" - resolved "https://registry.yarnpkg.com/subtext/-/subtext-4.4.1.tgz#2fcec945de429283c3d18b151ff0fa1f1b87aec9" - dependencies: - boom "5.x.x" - content "3.x.x" - hoek "4.x.x" - pez "2.x.x" - wreck "12.x.x" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - -swagger-methods@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/swagger-methods/-/swagger-methods-1.0.0.tgz#b39c77957d305a6535c0a1e015081185b99d61fc" - -swagger-parser@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/swagger-parser/-/swagger-parser-3.4.1.tgz#0290529dbae254d178b442a95df60d23d142301d" - dependencies: - call-me-maybe "^1.0.1" - debug "^2.2.0" - es6-promise "^3.0.2" - json-schema-ref-parser "^1.4.1" - ono "^2.0.1" - swagger-methods "^1.0.0" - swagger-schema-official "2.0.0-bab6bed" - z-schema "^3.16.1" - -swagger-schema-official@2.0.0-bab6bed: - version "2.0.0-bab6bed" - resolved "https://registry.yarnpkg.com/swagger-schema-official/-/swagger-schema-official-2.0.0-bab6bed.tgz#70070468d6d2977ca5237b2e519ca7d06a2ea3fd" - -table@^3.7.8: - version "3.8.3" - resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" - dependencies: - ajv "^4.7.0" - ajv-keywords "^1.0.0" - chalk "^1.1.1" - lodash "^4.0.0" - slice-ansi "0.0.4" - string-width "^2.0.0" - -table@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/table/-/table-4.0.1.tgz#a8116c133fac2c61f4a420ab6cdf5c4d61f0e435" - dependencies: - ajv "^4.7.0" - ajv-keywords "^1.0.0" - chalk "^1.1.1" - lodash "^4.0.0" - slice-ansi "0.0.4" - string-width "^2.0.0" - -tabula@1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/tabula/-/tabula-1.9.0.tgz#93c2017f628d159442c74933e170639490708b78" - dependencies: - assert-plus "^1.0.0" - dashdash "1.x >=1.8" - lstream "0.0.4" - -tar-fs@~1.12.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.12.0.tgz#a6a80553d8a54c73de1d0ae0e79de77035605e1d" - dependencies: - mkdirp "^0.5.0" - pump "^1.0.0" - tar-stream "^1.1.2" - -tar-stream@^1.1.2: - version "1.5.4" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.4.tgz#36549cf04ed1aee9b2a30c0143252238daf94016" - dependencies: - bl "^1.0.0" - end-of-stream "^1.0.0" - readable-stream "^2.0.0" - xtend "^4.0.0" - -text-table@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - -throat@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/throat/-/throat-4.0.0.tgz#e8d397aeb3f335c3bae404a83dc264b813a41e1b" - -"through@>=2.2.7 <3", through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - -tmp@^0.0.31: - version "0.0.31" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" - dependencies: - os-tmpdir "~1.0.1" - -topo@2.x.x: - version "2.0.2" - resolved "https://registry.yarnpkg.com/topo/-/topo-2.0.2.tgz#cd5615752539057c0dc0491a621c3bc6fbe1d182" - dependencies: - hoek "4.x.x" - -toppsy@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/toppsy/-/toppsy-1.1.0.tgz#dd1ef4a15aaa7ef077bc41e9851aa75851610b41" - dependencies: - oppsy "^1.0.2" - putmetric "^1.0.1" - -triton-watch@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/triton-watch/-/triton-watch-1.1.0.tgz#d2b47fbf6a45174198c196152bb86c696a923c35" - dependencies: - triton "5.2.x" - -triton@5.2.x, triton@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/triton/-/triton-5.2.0.tgz#16ce8cb155c37785b6818e403f5cf83934921d41" - dependencies: - assert-plus "0.2.0" - backoff "2.4.1" - bigspinner "3.1.0" - bunyan "1.5.1" - cmdln "4.1.2" - extsprintf "1.0.2" - getpass "0.1.6" - lomstream "1.1.0" - mkdirp "0.5.1" - once "1.3.2" - read "1.0.7" - restify-clients "1.5.0" - restify-errors "3.0.0" - rimraf "2.4.4" - semver "5.1.0" - smartdc-auth "2.5.2" - sshpk "1.10.2" - sshpk-agent "1.4.2" - strsplit "1.0.0" - tabula "1.9.0" - vasync "1.6.3" - verror "1.6.0" - which "1.2.4" - wordwrap "1.0.0" - -tryit@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" - -tunnel-agent@^0.4.0: - version "0.4.3" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - dependencies: - prelude-ls "~1.1.2" - -typedarray@^0.0.6, typedarray@~0.0.5: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - -uglify-js@^2.6: - version "2.8.29" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" - dependencies: - source-map "~0.5.1" - yargs "~3.10.0" - optionalDependencies: - uglify-to-browserify "~1.0.0" - -uglify-to-browserify@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" - -underscore@1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.3.3.tgz#47ac53683daf832bfa952e1774417da47817ae42" - -user-home@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" - dependencies: - os-homedir "^1.0.0" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - -uuid@^3.0.0, uuid@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" - -validator@^6.0.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/validator/-/validator-6.3.0.tgz#47ce23ed8d4eaddfa9d4b8ef0071b6cf1078d7c8" - -vasync@1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/vasync/-/vasync-1.4.3.tgz#c86d52e2b71613d29eedf159f3135dbe749cee37" - dependencies: - jsprim "0.3.0" - verror "1.1.0" - -vasync@1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/vasync/-/vasync-1.6.3.tgz#4a69d7052a47f4ce85503d7641df1cbf40432a94" - dependencies: - verror "1.6.0" - -vasync@^1.6.4: - version "1.6.4" - resolved "https://registry.yarnpkg.com/vasync/-/vasync-1.6.4.tgz#dfe93616ad0e7ae801b332a9d88bfc5cdc8e1d1f" - dependencies: - verror "1.6.0" - -verror@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.1.0.tgz#2a4b4eb14a207051e75a6f94ee51315bf173a1b0" - dependencies: - extsprintf "1.0.0" - -verror@1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.3.tgz#8a6a4ac3a8c774b6f687fece49bdffd78552e2cd" - dependencies: - extsprintf "1.0.0" - -verror@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" - dependencies: - extsprintf "1.0.2" - -verror@1.6.0, verror@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.6.0.tgz#7d13b27b1facc2e2da90405eb5ea6e5bdd252ea5" - dependencies: - extsprintf "1.2.0" - -vise@2.x.x: - version "2.0.2" - resolved "https://registry.yarnpkg.com/vise/-/vise-2.0.2.tgz#6b08e8fb4cb76e3a50cd6dd0ec37338e811a0d39" - dependencies: - hoek "4.x.x" - -vision@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/vision/-/vision-4.1.1.tgz#e1b612b2d2e2f20310a039290fd49d51248f82da" - dependencies: - boom "4.x.x" - hoek "4.x.x" - items "2.x.x" - joi "10.x.x" - -vstream@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/vstream/-/vstream-0.1.0.tgz#13587190f34e72ba7a07ebbaa7e70ac147b1fb7d" - dependencies: - assert-plus "0.1.5" - extsprintf "1.2.0" - -which@1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/which/-/which-1.2.4.tgz#1557f96080604e5b11b3599eb9f45b50a9efd722" - dependencies: - is-absolute "^0.1.7" - isexe "^1.1.1" - -window-size@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" - -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - -wordwrap@1.0.0, wordwrap@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - -wreck@10.x.x: - version "10.0.0" - resolved "https://registry.yarnpkg.com/wreck/-/wreck-10.0.0.tgz#98ab882f85e16a526332507f101f5a7841162278" - dependencies: - boom "4.x.x" - hoek "4.x.x" - -wreck@12.x.x: - version "12.2.2" - resolved "https://registry.yarnpkg.com/wreck/-/wreck-12.2.2.tgz#e21823d34c36d672004eefa347ae8c4f6050e3db" - dependencies: - boom "5.x.x" - hoek "4.x.x" - -write@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" - dependencies: - mkdirp "^0.5.1" - -xtend@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - -yallist@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - -yamljs@^0.2.10: - version "0.2.10" - resolved "https://registry.yarnpkg.com/yamljs/-/yamljs-0.2.10.tgz#481cc7c25ca73af59f591f0c96e3ce56c757a40f" - dependencies: - argparse "^1.0.7" - glob "^7.0.5" - -yargs@~3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" - dependencies: - camelcase "^1.0.2" - cliui "^2.1.0" - decamelize "^1.0.0" - window-size "0.1.0" - -z-schema@^3.16.1, z-schema@^3.17.0: - version "3.18.2" - resolved "https://registry.yarnpkg.com/z-schema/-/z-schema-3.18.2.tgz#e422196b5efe60b46adef3c3f2aef2deaa911161" - dependencies: - lodash.get "^4.1.2" - lodash.isequal "^4.4.0" - validator "^6.0.0" - optionalDependencies: - commander "^2.7.1" - -zerorpc@^0.9.7: - version "0.9.7" - resolved "https://registry.yarnpkg.com/zerorpc/-/zerorpc-0.9.7.tgz#64ddb32ce8c934bea5434ec81ca22e971045a860" - dependencies: - msgpack "1.0.2" - underscore "1.3.3" - uuid "^3.0.0" - zmq "2.x" - -zmq@2.x: - version "2.15.3" - resolved "https://registry.yarnpkg.com/zmq/-/zmq-2.15.3.tgz#66c6de82cc36b09734b820703776490a6fbbe624" - dependencies: - bindings "~1.2.1" - nan "~2.3.0" diff --git a/scripts/release b/scripts/release index 10f3152e..9873ee10 100755 --- a/scripts/release +++ b/scripts/release @@ -46,7 +46,8 @@ const tasks = [ task: [ { title: 'Branch', - description: 'Checks if the current branch is `master`. To ignore use the `--any-branch` flag', + description: + 'Checks if the current branch is `master`. To ignore use the `--any-branch` flag', filter: () => !argv['any-branch'], task: async () => { const branch = await execa.stdout('git', [ @@ -64,7 +65,8 @@ const tasks = [ }, { title: 'Working tree', - description: 'Checks if working tree is clean. To ignore use the `--force` flag', + description: + 'Checks if working tree is clean. To ignore use the `--force` flag', filter: () => !argv.force, task: async () => { const status = await execa.stdout('git', ['status', '--porcelain']); @@ -105,7 +107,8 @@ const tasks = [ }, { title: 'Publish', - description: 'Publish updated packages, based on the changes from last tag', + description: + 'Publish updated packages, based on the changes from last tag', task: async ({ prefix }) => { const { publish } = await inquirer.prompt([ { @@ -176,7 +179,11 @@ const tasks = [ name: 'release', type: 'confirm', default: false, - message: `${prefix}No lerna publish detected. Are you sure you want to release? \n ${prefix}${chalk.dim(`(${chalk.yellow(figures.warning)} this can have negative effects on future lerna publishes since it detects changes based on tags)`)}` + message: `${prefix}No lerna publish detected. Are you sure you want to release? \n ${prefix}${chalk.dim( + `(${chalk.yellow( + figures.warning + )} this can have negative effects on future lerna publishes since it detects changes based on tags)` + )}` } ]) }, @@ -348,16 +355,20 @@ const tasks = [ const tagBody = `${EOL}${lastCommits}`; console.log( - `${prefix}${chalk.yellow('Tag Name: ')}\n${prefix}${prefix}${chalk.dim(tagName)}` + `${prefix}${chalk.yellow( + 'Tag Name: ' + )}\n${prefix}${prefix}${chalk.dim(tagName)}` ); console.log(`${prefix}${chalk.yellow('Tag Description: ')}`); console.log( - `${chalk.dim(lastCommits + `${chalk.dim( + lastCommits .split(/\n/) .map(line => `${prefix}${prefix}${line}`) - .join('\n'))}` + .join('\n') + )}` ); const { createTag } = await inquirer.prompt([ @@ -393,7 +404,9 @@ const tasks = [ { name: 'pushTag', type: 'confirm', - message: `${prefix}Should ${chalk.yellow(tagName)} be pushed to origin?` + message: `${prefix}Should ${chalk.yellow( + tagName + )} be pushed to origin?` } ]); diff --git a/yarn.lock b/yarn.lock index edae14d5..a59290e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2167,6 +2167,13 @@ consulite@1.6.x: or-promise "1.x.x" wreck "10.x.x" +consulite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/consulite/-/consulite-2.0.0.tgz#4d35228d24f70a8fb01fdb8ed921d4e54d8bbb16" + dependencies: + or-promise "1.x.x" + wreck "12.x.x" + contains-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a"