mirror of
https://github.com/yldio/copilot.git
synced 2024-12-28 21:00:05 +02:00
chore: data layer
This commit is contained in:
parent
cb1dfa20f8
commit
cf5f476898
42
.yarnclean
Normal file
42
.yarnclean
Normal file
@ -0,0 +1,42 @@
|
||||
# test directories
|
||||
__tests__
|
||||
test
|
||||
tests
|
||||
powered-test
|
||||
|
||||
# asset directories
|
||||
docs
|
||||
doc
|
||||
website
|
||||
images
|
||||
assets
|
||||
|
||||
# examples
|
||||
example
|
||||
examples
|
||||
|
||||
# code coverage directories
|
||||
coverage
|
||||
.nyc_output
|
||||
|
||||
# build scripts
|
||||
Makefile
|
||||
Gulpfile.js
|
||||
Gruntfile.js
|
||||
|
||||
# configs
|
||||
.tern-project
|
||||
.gitattributes
|
||||
.editorconfig
|
||||
.*ignore
|
||||
.eslintrc
|
||||
.jshintrc
|
||||
.flowconfig
|
||||
.documentup.json
|
||||
.yarn-metadata.json
|
||||
.*.yml
|
||||
*.yml
|
||||
|
||||
# misc
|
||||
*.gz
|
||||
*.md
|
@ -15,9 +15,7 @@
|
||||
"good": "^7.2.0",
|
||||
"good-console": "^6.4.0",
|
||||
"good-squeeze": "^5.0.2",
|
||||
"graphql": "^0.9.6",
|
||||
"graphql-server-hapi": "^0.7.2",
|
||||
"graphql-tools": "^0.11.0",
|
||||
"graphi": "^2.0.0",
|
||||
"hapi": "^16.1.1",
|
||||
"joi": "^10.5.0",
|
||||
"joyent-cp-gql-schema": "^1.0.4"
|
||||
|
@ -1,6 +1,5 @@
|
||||
const schema = require('joyent-cp-gql-schema');
|
||||
const { graphqlHapi, graphiqlHapi } = require('graphql-server-hapi');
|
||||
const { makeExecutableSchema } = require('graphql-tools');
|
||||
const graphi = require('graphi');
|
||||
const Good = require('good');
|
||||
const Hapi = require('hapi');
|
||||
const resolvers = require('./resolvers');
|
||||
@ -45,27 +44,12 @@ server.register(
|
||||
}
|
||||
},
|
||||
{
|
||||
register: graphiqlHapi,
|
||||
register: graphi,
|
||||
options: {
|
||||
path: '/graphiql',
|
||||
graphiqlOptions: {
|
||||
endpointURL: '/graphql'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
register: graphqlHapi,
|
||||
options: {
|
||||
path: '/graphql',
|
||||
graphqlOptions: {
|
||||
schema: makeExecutableSchema({
|
||||
typeDefs: schema.sync(),
|
||||
resolvers
|
||||
})
|
||||
},
|
||||
route: {
|
||||
cors: true
|
||||
}
|
||||
graphqlPath: '/graphql',
|
||||
graphiqlPath: '/graphiql',
|
||||
schema,
|
||||
resolvers
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -2,60 +2,10 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@types/boom@*":
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/boom/-/boom-4.3.2.tgz#8f40fb142322958ff1bb520c2d8d20cf1dd3d468"
|
||||
|
||||
"@types/catbox@*":
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/catbox/-/catbox-7.1.0.tgz#adb0962f1cd9ba14d15efc0de91503364845e5f6"
|
||||
dependencies:
|
||||
"@types/boom" "*"
|
||||
|
||||
"@types/graphql@^0.9.0":
|
||||
version "0.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.9.1.tgz#b04ebe84bc997cc60dbea2ed4d0d4342c737f99d"
|
||||
|
||||
"@types/hapi@^16.0.0":
|
||||
version "16.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/hapi/-/hapi-16.1.2.tgz#e782b399a11ef8934c3de3ab07aae9fb53cb0968"
|
||||
dependencies:
|
||||
"@types/boom" "*"
|
||||
"@types/catbox" "*"
|
||||
"@types/joi" "*"
|
||||
"@types/mimos" "*"
|
||||
"@types/node" "*"
|
||||
"@types/podium" "*"
|
||||
"@types/shot" "*"
|
||||
|
||||
"@types/joi@*":
|
||||
version "10.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/joi/-/joi-10.3.2.tgz#bc4ce6577c294710663d347ebe59087f375075a5"
|
||||
|
||||
"@types/mime-db@*":
|
||||
version "1.27.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/mime-db/-/mime-db-1.27.0.tgz#9bc014a1fd1fdf47649c1a54c6dd7966b8284792"
|
||||
|
||||
"@types/mimos@*":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/mimos/-/mimos-3.0.0.tgz#20356e7381e9529c2a7e764c798be5a48cdeff96"
|
||||
dependencies:
|
||||
"@types/mime-db" "*"
|
||||
|
||||
"@types/node@*":
|
||||
version "7.0.22"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.22.tgz#4593f4d828bdd612929478ea40c67b4f403ca255"
|
||||
|
||||
"@types/podium@*":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/podium/-/podium-1.0.0.tgz#bfaa2151be2b1d6109cc69f7faa9dac2cba3bb20"
|
||||
|
||||
"@types/shot@*":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/shot/-/shot-3.4.0.tgz#459477c5187d3ebd303660ab099e7e9e0f3b656f"
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
abbrev@1:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f"
|
||||
@ -229,7 +179,7 @@ boom@2.x.x:
|
||||
dependencies:
|
||||
hoek "2.x.x"
|
||||
|
||||
boom@4.x.x, boom@^4.3.1:
|
||||
boom@4.x.x:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31"
|
||||
dependencies:
|
||||
@ -439,10 +389,6 @@ delegates@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
|
||||
|
||||
deprecated-decorator@^0.1.6:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz#00966317b7a12fe92f3cc831f7583af329b86c37"
|
||||
|
||||
doctrine@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63"
|
||||
@ -886,38 +832,26 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2:
|
||||
version "4.1.11"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
|
||||
|
||||
graphql-server-core@^0.7.0:
|
||||
graphi@^2.0.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/graphi/-/graphi-2.2.1.tgz#234a4752e09accd182e5b6cb93d59c75535702d8"
|
||||
dependencies:
|
||||
boom "4.x.x"
|
||||
graphql "0.9.x"
|
||||
graphql-server-core "0.7.x"
|
||||
graphql-server-module-graphiql "0.7.x"
|
||||
|
||||
graphql-server-core@0.7.x:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql-server-core/-/graphql-server-core-0.7.0.tgz#a658b3b0704f8a19b24254b64bef6b3d0ddce8ba"
|
||||
optionalDependencies:
|
||||
"@types/graphql" "^0.9.0"
|
||||
|
||||
graphql-server-hapi@^0.7.2:
|
||||
version "0.7.2"
|
||||
resolved "https://registry.yarnpkg.com/graphql-server-hapi/-/graphql-server-hapi-0.7.2.tgz#2fa55b8a000952f125af038404563e72d0ba05ab"
|
||||
dependencies:
|
||||
boom "^4.3.1"
|
||||
graphql-server-core "^0.7.0"
|
||||
graphql-server-module-graphiql "^0.7.2"
|
||||
optionalDependencies:
|
||||
"@types/graphql" "^0.9.0"
|
||||
"@types/hapi" "^16.0.0"
|
||||
|
||||
graphql-server-module-graphiql@^0.7.2:
|
||||
graphql-server-module-graphiql@0.7.x:
|
||||
version "0.7.2"
|
||||
resolved "https://registry.yarnpkg.com/graphql-server-module-graphiql/-/graphql-server-module-graphiql-0.7.2.tgz#aa1f2a26eadbf7127c1b077e633d5086da52b330"
|
||||
|
||||
graphql-tools@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-0.11.0.tgz#14c372f6ddad7e63a757094d541a937d6b31b7da"
|
||||
dependencies:
|
||||
deprecated-decorator "^0.1.6"
|
||||
lodash "^4.3.0"
|
||||
uuid "^3.0.1"
|
||||
optionalDependencies:
|
||||
"@types/graphql" "^0.9.0"
|
||||
|
||||
graphql@^0.9.6:
|
||||
graphql@0.9.x:
|
||||
version "0.9.6"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.9.6.tgz#514421e9d225c29dfc8fd305459abae58815ef2c"
|
||||
dependencies:
|
||||
@ -2149,7 +2083,7 @@ uuid@^2.0.1:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
|
||||
|
||||
uuid@^3.0.0, uuid@^3.0.1:
|
||||
uuid@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
const path = require('path');
|
||||
const { readFile } = require('mz/fs');
|
||||
const { readFileSync } = require('fs');
|
||||
|
||||
const file = path.join(__dirname, 'schema.gql');
|
||||
|
||||
module.exports = () => readFile(file, 'utf-8');
|
||||
module.exports.sync = () => readFileSync(file, 'utf-8');
|
||||
module.exports = readFileSync(file, 'utf-8');
|
||||
|
@ -9,9 +9,7 @@
|
||||
"lint": "eslint . --fix --format=tap",
|
||||
"test": "exit 0"
|
||||
},
|
||||
"dependencies": {
|
||||
"mz": "^2.6.0"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-config-joyent-portal": "1.0.0"
|
||||
|
@ -9,7 +9,7 @@
|
||||
}
|
||||
|
||||
type User {
|
||||
uuid: ID!
|
||||
id: ID!
|
||||
firstName: String!
|
||||
lastName: String!
|
||||
email: String!
|
||||
@ -17,7 +17,7 @@
|
||||
}
|
||||
|
||||
type DeploymentGroup {
|
||||
uuid: ID!
|
||||
id: ID!
|
||||
name: String!
|
||||
slug: String!
|
||||
services(slug: String): [Service]!
|
||||
@ -26,7 +26,7 @@
|
||||
}
|
||||
|
||||
type ServiceScale {
|
||||
uuid: ID!
|
||||
id: ID!
|
||||
serviceName: String!
|
||||
replicas: Int!
|
||||
}
|
||||
@ -39,14 +39,14 @@
|
||||
}
|
||||
|
||||
type ConvergenceAction {
|
||||
uuid: String!
|
||||
id: String!
|
||||
type: ConvergenceActionType!
|
||||
service: String! # service name
|
||||
machines: [String]! # instance machine ids
|
||||
}
|
||||
|
||||
type StateConvergencePlan {
|
||||
uuid: String!
|
||||
id: String!
|
||||
running: Boolean!
|
||||
actions: [ConvergenceAction]!
|
||||
}
|
||||
@ -69,7 +69,7 @@
|
||||
}
|
||||
|
||||
type Manifest {
|
||||
uuid: String!
|
||||
id: String!
|
||||
created: Date!
|
||||
type: ManifestType!
|
||||
format: ManifestFormat!
|
||||
@ -79,21 +79,21 @@
|
||||
|
||||
# immutable
|
||||
type Service {
|
||||
uuid: String! # unique id for db row
|
||||
id: String! # unique id for db row
|
||||
hash: String! # unique id for version of service
|
||||
name: String! # human readable name
|
||||
slug: String!
|
||||
instances: [Instance]!
|
||||
# metrics: [MetricType]!
|
||||
currentMetrics: [CurrentMetric]!
|
||||
connections: [String!] # list of serviceUuids
|
||||
parent: ID # parent service uuid
|
||||
connections: [String!] # list of serviceIds
|
||||
parent: ID # parent service id
|
||||
package: Package! # we don't have this in current mock data
|
||||
}
|
||||
|
||||
# for metrics max / min (I guess)
|
||||
type Package {
|
||||
uuid: ID!
|
||||
id: ID!
|
||||
name: String!
|
||||
type: String!
|
||||
memory: Float!
|
||||
@ -115,7 +115,7 @@
|
||||
}
|
||||
|
||||
type Instance {
|
||||
uuid: String!
|
||||
id: String!
|
||||
name: String!
|
||||
machineId: String!
|
||||
status: InstanceStatus!
|
||||
@ -123,8 +123,9 @@
|
||||
}
|
||||
|
||||
type Datacenter {
|
||||
uuid: String!
|
||||
# name: String! # Do we have 'official' human readable names?
|
||||
id: String!
|
||||
url: String!
|
||||
name: String!
|
||||
region: String!
|
||||
}
|
||||
|
||||
@ -140,7 +141,7 @@
|
||||
}
|
||||
|
||||
type MetricType {
|
||||
uuid: String!
|
||||
id: String!
|
||||
name: String!
|
||||
id: String!
|
||||
}
|
||||
@ -154,13 +155,13 @@
|
||||
type Query {
|
||||
portal: Portal
|
||||
deploymentGroups: [DeploymentGroup]
|
||||
deploymentGroup(uuid: String, slug: String): DeploymentGroup
|
||||
services(deploymentGroupUuid: String, deploymentGroupSlug: String): [Service]
|
||||
service(uuid: String, slug: String): Service
|
||||
instances(serviceUuid: String, serviceSlug: String): [Instance]
|
||||
instance(uuid: String, machineId: String): Instance
|
||||
deploymentGroup(id: String, slug: String): DeploymentGroup
|
||||
services(deploymentGroupId: String, deploymentGroupSlug: String): [Service]
|
||||
service(id: String, slug: String): Service
|
||||
instances(serviceId: String, serviceSlug: String): [Instance]
|
||||
instance(id: String, machineId: String): Instance
|
||||
metricTypes: [MetricType]
|
||||
metricData(instanceUuid: String!, metricType: String!, from: Date!, to: Date!): [InstanceMetric]!
|
||||
metricData(instanceId: String!, metricType: String!, from: Date!, to: Date!): [InstanceMetric]!
|
||||
package: Package
|
||||
datacenters: [Datacenter]
|
||||
# tmp test
|
||||
@ -173,41 +174,42 @@
|
||||
portal: Portal
|
||||
user: User
|
||||
deploymentGroups(name: String, slug: String): [DeploymentGroup]
|
||||
deploymentGroup(uuid: ID, name: String, slug: String): DeploymentGroup
|
||||
serviceScales(serviceName: String, versionUuid: ID): [ServiceScale]
|
||||
serviceScale(uuid: ID!): ServiceScale
|
||||
convergenceActions(type: ConvergenceActionType, service: String, versionUuid: ID): [ConvergenceAction]
|
||||
convergenceAction(uuid: ID!): ConvergenceAction
|
||||
stateConvergencePlans(running: Boolean, versionUuid: ID): [StateConvergencePlan]
|
||||
stateConvergencePlan(uuid: ID!): StateConvergencePlan
|
||||
versions(manifestUuid: ID, deploymentGroupUuid: ID): [Version]
|
||||
version(uuid: ID, manifestUuid: ID): Version
|
||||
manifests(type: String, deploymentGroupUuid: ID): [Manifest]
|
||||
manifest(uuid: ID!): Manifest
|
||||
services(name: String, slug: String, parentUuid: ID, deploymentGroupUuid: ID, deploymentGroupSlug: String): [Service]
|
||||
service(uuid: ID, hash: ID): Service
|
||||
deploymentGroup(id: ID, name: String, slug: String): DeploymentGroup
|
||||
serviceScales(serviceName: String, versionId: ID): [ServiceScale]
|
||||
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]
|
||||
manifest(id: ID!): Manifest
|
||||
services(name: String, slug: String, parentId: ID, deploymentGroupId: ID, deploymentGroupSlug: String): [Service]
|
||||
service(id: ID, hash: ID): Service
|
||||
packages(name: String, type: String, memory: Int, disk: Int, swap: Int, lwps: Int, vcpus: Int, version: String, group: String): [Package]
|
||||
package(uuid: ID!): Package
|
||||
instances(name: String!, machineId: ID, status: InstanceStatus, serviceUuid: ID, serviceSlug: String, deploymentGroupUuid: ID, deploymentGroupSlug: String): [Instance]
|
||||
instance(uuid: ID!): Instance
|
||||
datacenter(uuid: ID, region: String): Datacenter
|
||||
package(id: ID!): Package
|
||||
instances(name: String!, machineId: ID, status: InstanceStatus, serviceId: ID, serviceSlug: String, deploymentGroupId: ID, deploymentGroupSlug: String): [Instance]
|
||||
instance(id: ID!): Instance
|
||||
datacenter(id: ID, region: String): Datacenter
|
||||
datacenters: [Datacenter]
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createDeploymentGroup(name: String!) : DeploymentGroup
|
||||
updateDeploymentGroup(uuid: ID!, name: String!) : DeploymentGroup
|
||||
updateDeploymentGroup(id: ID!, name: String!) : DeploymentGroup
|
||||
|
||||
provisionManifest(deploymentGroupUuid: ID!, type: ManifestType!, format: ManifestFormat!, raw: String!) : Version
|
||||
provisionManifest(deploymentGroupId: ID!, type: ManifestType!, format: ManifestFormat!, raw: String!) : Version
|
||||
scale(service: ID!, replicas: Int!) : Version
|
||||
|
||||
stopServices(uuids: [ID]!) : [Service]
|
||||
startServices(uuids: [ID]!) : [Service]
|
||||
restartServices(uuids: [ID]!) : [Service]
|
||||
deleteServices(uuids: [ID]!) : [Service]
|
||||
stopServices(ids: [ID]!) : [Service]
|
||||
startServices(ids: [ID]!) : [Service]
|
||||
restartServices(ids: [ID]!) : [Service]
|
||||
deleteServices(ids: [ID]!) : [Service]
|
||||
|
||||
stopInstances(uuids: [ID]!) : [Instance]
|
||||
startInstances(uuids: [ID]!) : [Instance]
|
||||
restartInstances(uuids: [ID]!) : [Instance]
|
||||
stopInstances(ids: [ID]!) : [Instance]
|
||||
startInstances(ids: [ID]!) : [Instance]
|
||||
restartInstances(ids: [ID]!) : [Instance]
|
||||
|
||||
# reprovision() ???
|
||||
}
|
||||
|
@ -1,82 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// Deployments
|
||||
|
||||
exports.deploymentCreate = function (request, reply) {
|
||||
const deploymentRoute = request.server.lookup('deploymentGet');
|
||||
|
||||
this.createDeployment(request.payload).then((deployment) => {
|
||||
reply(deployment).created(deploymentRoute.path.replace('{deployment}', deployment.id));
|
||||
}).catch((error) => {
|
||||
reply(error);
|
||||
});
|
||||
};
|
||||
|
||||
exports.deploymentGet = function (request, reply) {
|
||||
reply(this.getDeployment(request.params.deploymentId));
|
||||
};
|
||||
|
||||
exports.deploymentUpdate = function (request, reply) {
|
||||
const payload = request.payload;
|
||||
payload.id = request.params.deploymentId;
|
||||
|
||||
reply(this.updateDeployment(payload));
|
||||
};
|
||||
|
||||
exports.deploymentDelete = function (request, reply) {
|
||||
reply(this.deleteDeployment(request.params.deploymentId));
|
||||
};
|
||||
|
||||
exports.deploymentsGet = function (request, reply) {
|
||||
reply(this.getDeployments());
|
||||
};
|
||||
|
||||
|
||||
// Datacenters
|
||||
|
||||
exports.datacentersGet = function (request, reply) {
|
||||
reply(this.getDatacenters());
|
||||
};
|
||||
|
||||
|
||||
// Manifests
|
||||
|
||||
exports.manifestCreate = function (request, reply) {
|
||||
const manifestRoute = request.server.lookup('manifestGet');
|
||||
const deploymentId = request.params.deploymentId;
|
||||
|
||||
this.createManifest(deploymentId, request.payload).then((manifest) => {
|
||||
reply(manifest).created(manifestRoute.path.replace('{deployment}', deploymentId).replace('{manifestId}', manifest.id));
|
||||
}).catch((error) => {
|
||||
reply(error);
|
||||
});
|
||||
};
|
||||
|
||||
exports.manifestGet = function (request, reply) {
|
||||
reply(this.getManifest(request.params.manifestId));
|
||||
};
|
||||
|
||||
|
||||
// Activities and Metrics
|
||||
|
||||
exports.activitiesGet = function (request, reply) {
|
||||
reply(this.getActivities(request.params.deploymentId));
|
||||
};
|
||||
|
||||
exports.metricsGet = function (request, reply) {
|
||||
reply(this.getMetrics(request.params.deploymentId));
|
||||
};
|
||||
|
||||
|
||||
// Services
|
||||
|
||||
exports.servicesGet = function (request, reply) {
|
||||
reply(this.getServices(request.params.deploymentId));
|
||||
};
|
||||
|
||||
exports.serviceUpdate = function (request, reply) {
|
||||
const service = request.payload;
|
||||
service.name = request.params.name;
|
||||
|
||||
reply(this.updateService(request.params.deploymentId, service));
|
||||
};
|
@ -1,10 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
const Schema = require('joyent-cp-gql-schema');
|
||||
const Graphi = require('graphi');
|
||||
const PortalData = require('portal-data');
|
||||
const Graphql = require('./models/graphql');
|
||||
const Pack = require('../package.json');
|
||||
const Routes = require('./routes');
|
||||
const Resolvers = require('./resolvers');
|
||||
|
||||
|
||||
module.exports = function (server, options, next) {
|
||||
@ -19,12 +19,13 @@ module.exports = function (server, options, next) {
|
||||
server.register([
|
||||
{
|
||||
register: Graphi,
|
||||
options: Graphql.options(data)
|
||||
options: {
|
||||
schema: Schema,
|
||||
resolvers: Resolvers(data)
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
server.route(Routes);
|
||||
|
||||
next();
|
||||
});
|
||||
};
|
||||
|
@ -1,121 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
exports.activities = [
|
||||
{
|
||||
date: Date.now(),
|
||||
type: 'start',
|
||||
meta: {
|
||||
user: 'Tom'
|
||||
}
|
||||
},
|
||||
{
|
||||
date: Date.now(),
|
||||
type: 'stop',
|
||||
meta: {
|
||||
user: 'Dave'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
exports.datacenters = [
|
||||
{ name: 'us-sw-1', url: 'https://us-sw-1.api.joyentcloud.com' },
|
||||
{ name: 'us-west-1', url: 'https://us-west-1.api.joyentcloud.com' }
|
||||
];
|
||||
|
||||
|
||||
exports.services = [
|
||||
{
|
||||
name: 'consul',
|
||||
count: 3
|
||||
},
|
||||
{
|
||||
name: 'prometheus',
|
||||
count: 1
|
||||
}
|
||||
];
|
||||
|
||||
exports.service = exports.services[0];
|
||||
|
||||
|
||||
exports.deployments = [{
|
||||
id: 'd1f6c3af-1180-46cc-8d3f-1e7e90e5795d',
|
||||
name: 'User Services',
|
||||
datacenter: 'us-sw-1',
|
||||
state: {
|
||||
current: 'started'
|
||||
},
|
||||
services: exports.services
|
||||
}];
|
||||
|
||||
exports.deployment = exports.deployments[0];
|
||||
|
||||
|
||||
exports.manifest = {
|
||||
id: 'd1f6c3af-1180-46cc-8d3f-1e7e90e5795d',
|
||||
created: Date.now(),
|
||||
deploymentId: exports.deployment.id,
|
||||
type: 'docker-compose',
|
||||
format: 'yml',
|
||||
raw: `consul:
|
||||
image: autopilotpattern/consul:0.7.2-r0.8
|
||||
restart: always
|
||||
dns:
|
||||
- 127.0.0.1
|
||||
labels:
|
||||
- triton.cns.services=consul
|
||||
ports:
|
||||
- "8500:8500"
|
||||
command: >
|
||||
/usr/local/bin/containerpilot
|
||||
/bin/consul agent -server
|
||||
-config-dir=/etc/consul
|
||||
-log-level=err
|
||||
-bootstrap-expect 1
|
||||
-ui-dir /ui
|
||||
prometheus:
|
||||
image: autopilotpattern/prometheus:1.3.0r1.0
|
||||
mem_limit: 128m
|
||||
restart: always
|
||||
ports:
|
||||
- "9090:9090"`,
|
||||
obj: {
|
||||
consul: {
|
||||
image: 'autopilotpattern/consul:0.7.2-r0.8',
|
||||
restart: 'always',
|
||||
dns: ['127.0.0.1'],
|
||||
labels: ['triton.cns.services=consul'],
|
||||
ports: ['8500:8500'],
|
||||
command: `>
|
||||
/usr/local/bin/containerpilot
|
||||
/bin/consul agent -server
|
||||
-config-dir=/etc/consul
|
||||
-log-level=err
|
||||
-bootstrap-expect 1
|
||||
-ui-dir /ui`
|
||||
},
|
||||
prometheus: {
|
||||
image: 'autopilotpattern/prometheus:1.3.0r1.0',
|
||||
mem_limit: '128m',
|
||||
restart: 'always',
|
||||
ports: ['9090:9090']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
exports.metrics = [
|
||||
{
|
||||
service: 'consul',
|
||||
cpu: 1.2,
|
||||
memory: 23344523,
|
||||
network: 5024
|
||||
},
|
||||
{
|
||||
service: 'prometheus',
|
||||
cpu: 24.2,
|
||||
memory: 514234453,
|
||||
network: 10024
|
||||
}
|
||||
];
|
@ -1,89 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const Fs = require('fs');
|
||||
const Path = require('path');
|
||||
|
||||
|
||||
const internals = {
|
||||
schema: Fs.readFileSync(Path.join(__dirname, 'schema.gql')).toString()
|
||||
};
|
||||
|
||||
|
||||
exports.options = (data) => {
|
||||
const queryWrap = function (name) {
|
||||
return function (args, request) {
|
||||
return data[name](args);
|
||||
};
|
||||
};
|
||||
|
||||
const queries = [
|
||||
'portal',
|
||||
'deploymentGroups',
|
||||
'deploymentGroup',
|
||||
'services',
|
||||
'service',
|
||||
'instances',
|
||||
'instance',
|
||||
'metricTypes',
|
||||
'metricData',
|
||||
'package',
|
||||
'datacenters',
|
||||
'instanceMetric'
|
||||
];
|
||||
|
||||
const resolvers = {
|
||||
createDeploymentGroup: (args, request) => {
|
||||
return data.createDeploymentGroup(args.name);
|
||||
},
|
||||
|
||||
updateDeploymentGroup: (args, request) => {
|
||||
return data.updateDeploymentGroup(args.uuid, args.name);
|
||||
},
|
||||
|
||||
provisionManifest: (args, request) => {
|
||||
return data.provisionManifest(args.deploymentGroupUuid, args.type, args.format, args.raw);
|
||||
},
|
||||
|
||||
scale: (args, request) => {
|
||||
return data.scale(args.service, args.replicas);
|
||||
},
|
||||
|
||||
stopServices: (args, request) => {
|
||||
return data.stopServices(args.uuids);
|
||||
},
|
||||
|
||||
startServices: (args, request) => {
|
||||
return data.startServices(args.uuids);
|
||||
},
|
||||
|
||||
restartServices: (args, request) => {
|
||||
return data.restartServices(args.uuids);
|
||||
},
|
||||
|
||||
deleteServices: (args, request) => {
|
||||
return data.deleteServices(args.uuids);
|
||||
},
|
||||
|
||||
stopInstances: (args, request) => {
|
||||
return data.stopInstances(args.uuids);
|
||||
},
|
||||
|
||||
startInstances: (args, request) => {
|
||||
return data.startInstances(args.uuids);
|
||||
},
|
||||
|
||||
restartInstances: (args, request) => {
|
||||
return data.restartInstances(args.uuids);
|
||||
}
|
||||
};
|
||||
|
||||
queries.forEach((query) => {
|
||||
const functionName = 'get' + query[0].toUpperCase() + query.slice(1);
|
||||
resolvers[query] = queryWrap(functionName);
|
||||
});
|
||||
|
||||
return {
|
||||
schema: internals.schema,
|
||||
resolvers
|
||||
};
|
||||
};
|
@ -1,116 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const Joi = require('joi');
|
||||
const Examples = require('./examples');
|
||||
|
||||
|
||||
// Shared schema between schema sections
|
||||
|
||||
const internals = {
|
||||
serviceName: Joi.string().required().description('Unique name to identify the service')
|
||||
};
|
||||
|
||||
|
||||
// Activity
|
||||
|
||||
exports.activity = Joi.object({
|
||||
date: Joi.date().required().description('Date/time when the activity occurred'),
|
||||
type: Joi.string().required().description('The type of activity that occurred'),
|
||||
meta: Joi.object().optional().description('Any metadata related to the activity')
|
||||
}).example(Examples.activities[0]);
|
||||
|
||||
exports.activities = Joi.array().items(exports.activity).example(Examples.activities);
|
||||
|
||||
|
||||
// Datacenters
|
||||
|
||||
exports.datacenter = Joi.object({
|
||||
name: Joi.string().required().description('Name of datacenter'),
|
||||
url: Joi.string().required().description('URL of datacenter')
|
||||
}).example(Examples.datacenters[0]);
|
||||
|
||||
exports.datacenters = Joi.array().items(exports.datacenter).example(Examples.datacenters);
|
||||
|
||||
|
||||
// Services
|
||||
|
||||
exports.serviceName = internals.serviceName;
|
||||
|
||||
exports.serviceCount = Joi.number().default(1).description('Number of instances of the service');
|
||||
|
||||
exports.service = Joi.object({
|
||||
name: internals.serviceName,
|
||||
count: exports.serviceCount
|
||||
}).example(Examples.services[0]);
|
||||
|
||||
exports.services = Joi.array().items(exports.service).example(Examples.services);
|
||||
|
||||
exports.serviceUpdate = Joi.object({
|
||||
count: exports.serviceCount.required()
|
||||
});
|
||||
|
||||
|
||||
// State
|
||||
|
||||
exports.stateAction = Joi.object({
|
||||
action: Joi.string().required().valid(['start', 'stop', 'restart'])
|
||||
.description('Action being performed on the deployment group')
|
||||
});
|
||||
|
||||
exports.state = Joi.object({
|
||||
current: Joi.string().required().valid(['started', 'stopped']).default('stopped')
|
||||
.description('The current state of the deployment group')
|
||||
});
|
||||
|
||||
|
||||
// Deployments
|
||||
|
||||
exports.deploymentId = Joi.string().required().description('ID of deployment group');
|
||||
|
||||
exports.deploymentCreate = Joi.object({
|
||||
name: Joi.string().required().description('Name of deployment group'),
|
||||
datacenter: Joi.string().required().description('Datacenter the deployment group belongs to')
|
||||
});
|
||||
|
||||
exports.deploymentUpdate = Joi.object({
|
||||
name: Joi.string().optional().description('Name of deployment group'),
|
||||
datacenter: Joi.string().optional().description('Datacenter the deployment group belongs to')
|
||||
}).or('name', 'datacenter');
|
||||
|
||||
exports.deployment = exports.deploymentCreate.keys({
|
||||
id: exports.deploymentId,
|
||||
state: exports.state,
|
||||
services: exports.services
|
||||
}).example(Examples.deployments[0]);
|
||||
|
||||
exports.deployments = Joi.array().items(exports.deployment);
|
||||
|
||||
|
||||
// Manifests
|
||||
|
||||
exports.manifestId = Joi.string().required().description('ID of manifest').example(Examples.manifest.id);
|
||||
|
||||
exports.manifestCreate = Joi.object({
|
||||
format: Joi.string().default('yml').valid(['yml', 'json']).description('File format of raw data').example(Examples.manifest.format),
|
||||
type: Joi.string().default('docker-compose').valid(['docker-compose']).description('Type of manifest, e.g. docker-compose').example(Examples.manifest.type),
|
||||
raw: Joi.string().required().description('Original manifest file in a string form').example(Examples.manifest.raw),
|
||||
obj: Joi.object().required().description('Manifest file represented as JSON').example(Examples.manifest.obj)
|
||||
});
|
||||
|
||||
exports.manifest = exports.manifestCreate.keys({
|
||||
id: exports.manifestId,
|
||||
created: Joi.date().required().description('Date/time when the manifest was created').example(Examples.manifest.created),
|
||||
deploymentId: exports.deploymentId
|
||||
}).example(Examples.manifest);
|
||||
|
||||
|
||||
// Metrics
|
||||
|
||||
exports.metric = Joi.object({
|
||||
service: internals.serviceName,
|
||||
cpu: Joi.number().required().description('CPU usage percentage'),
|
||||
memory: Joi.number().required().description('Total memory usage in bytes'),
|
||||
network: Joi.number().required().description('Total bytes per second transferred by the NIC')
|
||||
}).example(Examples.metrics[0]);
|
||||
|
||||
exports.metrics = Joi.array().items(exports.metric).example(Examples.metrics);
|
@ -1,204 +0,0 @@
|
||||
|
||||
scalar Date
|
||||
scalar Object
|
||||
|
||||
type Portal {
|
||||
username: String!
|
||||
datacenter: Datacenter!
|
||||
deploymentGroups: [DeploymentGroup]!
|
||||
}
|
||||
|
||||
type DeploymentGroup {
|
||||
uuid: ID!
|
||||
name: String!
|
||||
slug: String!
|
||||
services: [Service]!
|
||||
version: Version!
|
||||
history: [Version]!
|
||||
}
|
||||
|
||||
type ServiceScale {
|
||||
uuid: ID!
|
||||
serviceName: String!
|
||||
replicas: Int!
|
||||
}
|
||||
|
||||
enum ConvergenceActionType {
|
||||
NOOP
|
||||
CREATE
|
||||
RECREATE
|
||||
START
|
||||
}
|
||||
|
||||
type ConvergenceAction {
|
||||
uuid: String!
|
||||
type: ConvergenceActionType!
|
||||
service: String! # service name
|
||||
machines: [String]! # instance machine ids
|
||||
}
|
||||
|
||||
type StateConvergencePlan {
|
||||
uuid: String!
|
||||
running: Boolean!
|
||||
actions: [ConvergenceAction]!
|
||||
}
|
||||
|
||||
type Version {
|
||||
created: Date! # Either Int or define scalar
|
||||
manifest: Manifest!
|
||||
scale: [ServiceScale]!
|
||||
plan: StateConvergencePlan
|
||||
}
|
||||
|
||||
enum ManifestType {
|
||||
COMPOSE
|
||||
MARIPOSA
|
||||
}
|
||||
|
||||
enum ManifestFormat {
|
||||
JSON
|
||||
YAML
|
||||
}
|
||||
|
||||
type Manifest {
|
||||
uuid: String!
|
||||
created: Date!
|
||||
type: ManifestType!
|
||||
format: ManifestFormat!
|
||||
raw: String!
|
||||
obj: Object!
|
||||
}
|
||||
|
||||
# immutable
|
||||
type Service {
|
||||
uuid: String! # unique id for db row
|
||||
hash: String! # unique id for version of service
|
||||
name: String! # human readable name
|
||||
slug: String!
|
||||
instances: [Instance]!
|
||||
# metrics: [MetricType]!
|
||||
currentMetrics: [CurrentMetric]!
|
||||
connections: [String!] # list of serviceUuids
|
||||
parent: ID # parent service uuid
|
||||
package: Package! # we don't have this in current mock data
|
||||
}
|
||||
|
||||
# for metrics max / min (I guess)
|
||||
type Package {
|
||||
uuid: ID!
|
||||
name: String!
|
||||
type: String!
|
||||
memory: Float!
|
||||
disk: Float!
|
||||
swap: Float!
|
||||
lwps: Int!
|
||||
vcpus: Int!
|
||||
version: String!
|
||||
group: String!
|
||||
}
|
||||
|
||||
enum InstanceStatus {
|
||||
CREATED
|
||||
RESTARTING
|
||||
RUNNING
|
||||
PAUSED
|
||||
EXITED
|
||||
DELETED
|
||||
}
|
||||
|
||||
type Instance {
|
||||
uuid: String!
|
||||
name: String!
|
||||
machineId: String!
|
||||
status: InstanceStatus!
|
||||
# metrics: [InstanceMetric]!
|
||||
}
|
||||
|
||||
type Datacenter {
|
||||
uuid: String!
|
||||
# name: String! # Do we have 'official' human readable names?
|
||||
region: String!
|
||||
}
|
||||
|
||||
type InstanceMetric {
|
||||
type: MetricType!
|
||||
data: [MetricData]!
|
||||
}
|
||||
|
||||
type CurrentMetric {
|
||||
name: String!
|
||||
value: Float!
|
||||
measurement: String!
|
||||
}
|
||||
|
||||
type MetricType {
|
||||
uuid: String!
|
||||
name: String!
|
||||
id: String!
|
||||
}
|
||||
|
||||
type MetricData {
|
||||
timestamp: Int!
|
||||
value: Float!
|
||||
}
|
||||
|
||||
# Need to review queries
|
||||
type Query {
|
||||
portal: Portal
|
||||
deploymentGroups: [DeploymentGroup]
|
||||
deploymentGroup(uuid: String, slug: String): DeploymentGroup
|
||||
services(deploymentGroupUuid: String, deploymentGroupSlug: String): [Service]
|
||||
service(uuid: String, slug: String): Service
|
||||
instances(serviceUuid: String, serviceSlug: String): [Instance]
|
||||
instance(uuid: String, machineId: String): Instance
|
||||
metricTypes: [MetricType]
|
||||
metricData(instanceUuid: String!, metricType: String!, from: Date!, to: Date!): [InstanceMetric]!
|
||||
package: Package
|
||||
datacenters: [Datacenter]
|
||||
# tmp test
|
||||
instanceMetric: InstanceMetric!
|
||||
}
|
||||
|
||||
# we probably wont use some of these queries or arguments
|
||||
# but this way we expose the entire db through gql
|
||||
type Query {
|
||||
portal: Portal
|
||||
deploymentGroups(name: String, slug: String): [DeploymentGroup]
|
||||
deploymentGroup(uuid: ID, name: String, slug: String): DeploymentGroup
|
||||
serviceScales(serviceName: String, versionUuid: ID): [ServiceScale]
|
||||
serviceScale(uuid: ID!): ServiceScale
|
||||
convergenceActions(type: ConvergenceActionType, service: String, versionUuid: ID): [ConvergenceAction]
|
||||
convergenceAction(uuid: ID!): ConvergenceAction
|
||||
stateConvergencePlans(running: Boolean, versionUuid: ID): [StateConvergencePlan]
|
||||
stateConvergencePlan(uuid: ID!): StateConvergencePlan
|
||||
versions(manifestUuid: ID, deploymentGroupUuid: ID): [Version]
|
||||
version(uuid: ID, manifestUuid: ID): Version
|
||||
manifests(type: String, deploymentGroupUuid: ID): [Manifest]
|
||||
manifest(uuid: ID!): Manifest
|
||||
services(name: String, slug: String, parentUuid: ID, deploymentGroupUuid: ID, deploymentGroupSlug: String): [Service]
|
||||
service(uuid: ID, hash: ID): Service
|
||||
packages(name: String, type: String, memory: Int, disk: Int, swap: Int, lwps: Int, vcpus: Int, version: String, group: String): [Package]
|
||||
package(uuid: ID!): Package
|
||||
instances(name: String!, machineId: ID, status: InstanceStatus, serviceUuid: ID, serviceSlug: String, deploymentGroupUuid: ID, deploymentGroupSlug: String): [Instance]
|
||||
instance(uuid: ID!): Instance
|
||||
datacenter(uuid: ID, region: String): Datacenter
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createDeploymentGroup(name: String!) : DeploymentGroup
|
||||
updateDeploymentGroup(uuid: ID!, name: String!) : DeploymentGroup
|
||||
|
||||
provisionManifest(deploymentGroupUuid: ID!, type: ManifestType!, format: ManifestFormat!, raw: String!) : Version
|
||||
scale(service: ID!, replicas: Int!) : Version
|
||||
|
||||
stopServices(uuids: [ID]!) : [Service]
|
||||
startServices(uuids: [ID]!) : [Service]
|
||||
restartServices(uuids: [ID]!) : [Service]
|
||||
deleteServices(uuids: [ID]!) : [Service]
|
||||
|
||||
stopInstances(uuids: [ID]!) : [Instance]
|
||||
startInstances(uuids: [ID]!) : [Instance]
|
||||
restartInstances(uuids: [ID]!) : [Instance]
|
||||
|
||||
# reprovision() ???
|
||||
}
|
57
packages/portal-api/lib/resolvers.js
Normal file
57
packages/portal-api/lib/resolvers.js
Normal file
@ -0,0 +1,57 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = (data) => {
|
||||
const queryWrap = function (name) {
|
||||
return function (options, request, cb) {
|
||||
return data[name](options, cb);
|
||||
};
|
||||
};
|
||||
|
||||
const mutationWrap = function (name) {
|
||||
return function (options, request, cb) {
|
||||
return data[name](options, cb);
|
||||
};
|
||||
};
|
||||
|
||||
const queries = [
|
||||
'portal',
|
||||
'deploymentGroups',
|
||||
'deploymentGroup',
|
||||
'services',
|
||||
'service',
|
||||
'instances',
|
||||
'instance',
|
||||
'metricTypes',
|
||||
'metricData',
|
||||
'package',
|
||||
'datacenters',
|
||||
'instanceMetric'
|
||||
];
|
||||
|
||||
const mutations = [
|
||||
'createDeploymentGroup',
|
||||
'updateDeploymentGroup',
|
||||
'provisionManifest',
|
||||
'scale',
|
||||
'stopServices',
|
||||
'startServices',
|
||||
'restartServices',
|
||||
'deleteServices',
|
||||
'stopInstances',
|
||||
'startInstances',
|
||||
'restartInstances'
|
||||
];
|
||||
|
||||
const resolvers = {};
|
||||
|
||||
queries.forEach((query) => {
|
||||
const functionName = 'get' + query[0].toUpperCase() + query.slice(1);
|
||||
resolvers[query] = queryWrap(functionName);
|
||||
});
|
||||
|
||||
mutations.forEach((mutation) => {
|
||||
resolvers[mutation] = mutationWrap(mutation);
|
||||
});
|
||||
|
||||
return resolvers;
|
||||
};
|
@ -1,224 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const Handlers = require('./handlers');
|
||||
const Models = require('./models');
|
||||
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
path: '/deployment',
|
||||
method: 'post',
|
||||
config: {
|
||||
tags: ['api', 'deployment'],
|
||||
description: 'Create new deployment group',
|
||||
validate: {
|
||||
payload: Models.deploymentCreate
|
||||
},
|
||||
response: {
|
||||
schema: Models.deployment
|
||||
},
|
||||
handler: Handlers.deploymentCreate,
|
||||
plugins: {
|
||||
'hapi-swagger': {
|
||||
responses: {
|
||||
'201': {
|
||||
description: 'Deployment group created',
|
||||
schema: Models.deployment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/deployment/{deploymentId}',
|
||||
method: 'get',
|
||||
config: {
|
||||
id: 'deploymentGet',
|
||||
tags: ['api', 'deployment'],
|
||||
description: 'Retrieve a deployment group',
|
||||
validate: {
|
||||
params: {
|
||||
deploymentId: Models.deploymentId
|
||||
}
|
||||
},
|
||||
response: {
|
||||
schema: Models.deployment
|
||||
},
|
||||
handler: Handlers.deploymentGet
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/deployment/{deploymentId}',
|
||||
method: 'put',
|
||||
config: {
|
||||
tags: ['api', 'deployment'],
|
||||
description: 'Update a deployment group',
|
||||
validate: {
|
||||
params: {
|
||||
deploymentId: Models.deploymentId
|
||||
},
|
||||
payload: Models.deploymentUpdate
|
||||
},
|
||||
response: {
|
||||
schema: Models.deployment
|
||||
},
|
||||
handler: Handlers.deploymentUpdate
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/deployment/{deploymentId}',
|
||||
method: 'delete',
|
||||
config: {
|
||||
tags: ['api', 'deployment'],
|
||||
description: 'Delete a deployment group',
|
||||
validate: {
|
||||
params: {
|
||||
deploymentId: Models.deploymentId
|
||||
}
|
||||
},
|
||||
handler: Handlers.deploymentDelete
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/deployments',
|
||||
method: 'get',
|
||||
config: {
|
||||
tags: ['api', 'deployment'],
|
||||
description: 'Retrieve a list of deployment groups',
|
||||
response: {
|
||||
schema: Models.deployments
|
||||
},
|
||||
handler: Handlers.deploymentsGet
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/datacenters',
|
||||
method: 'get',
|
||||
config: {
|
||||
tags: ['api', 'datacenter'],
|
||||
description: 'Retrieve a list of available datacenters',
|
||||
response: {
|
||||
schema: Models.datacenters
|
||||
},
|
||||
handler: Handlers.datacentersGet
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/deployment/{deploymentId}/manifest',
|
||||
method: 'post',
|
||||
config: {
|
||||
tags: ['api', 'deployment', 'manifest'],
|
||||
description: 'Create a new manifest revision for a deployment group',
|
||||
validate: {
|
||||
params: {
|
||||
deploymentId: Models.deploymentId
|
||||
},
|
||||
payload: Models.manifestCreate
|
||||
},
|
||||
response: {
|
||||
schema: Models.manifest
|
||||
},
|
||||
handler: Handlers.manifestCreate,
|
||||
plugins: {
|
||||
'hapi-swagger': {
|
||||
responses: {
|
||||
'201': {
|
||||
description: 'Manifest revision created',
|
||||
schema: Models.manifest
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/deployment/{deploymentId}/manifest/{manifestId}',
|
||||
method: 'get',
|
||||
config: {
|
||||
id: 'manifestGet',
|
||||
tags: ['api', 'deployment', 'manifest'],
|
||||
description: 'Retrieve a manifest revision for a deployment group',
|
||||
validate: {
|
||||
params: {
|
||||
deploymentId: Models.deploymentId,
|
||||
manifestId: Models.manifestId
|
||||
}
|
||||
},
|
||||
response: {
|
||||
schema: Models.manifest
|
||||
},
|
||||
handler: Handlers.manifestGet
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/deployment/{deploymentId}/activities',
|
||||
method: 'get',
|
||||
config: {
|
||||
tags: ['api', 'deployment', 'activity'],
|
||||
description: 'Retrieve the recent activities for the deployment group',
|
||||
validate: {
|
||||
params: {
|
||||
deploymentId: Models.deploymentId
|
||||
}
|
||||
},
|
||||
response: {
|
||||
schema: Models.activities
|
||||
},
|
||||
handler: Handlers.activitiesGet
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/deployment/{deploymentId}/metrics',
|
||||
method: 'get',
|
||||
config: {
|
||||
tags: ['api', 'deployment', 'metric'],
|
||||
description: 'Retrieve metrics for the deployment group',
|
||||
validate: {
|
||||
params: {
|
||||
deploymentId: Models.deploymentId
|
||||
}
|
||||
},
|
||||
response: {
|
||||
schema: Models.metrics
|
||||
},
|
||||
handler: Handlers.metricsGet
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/deployment/{deploymentId}/services',
|
||||
method: 'get',
|
||||
config: {
|
||||
tags: ['api', 'deployment', 'service'],
|
||||
description: 'Retrieve the services for a deployment group',
|
||||
validate: {
|
||||
params: {
|
||||
deploymentId: Models.deploymentId
|
||||
}
|
||||
},
|
||||
response: {
|
||||
schema: Models.services
|
||||
},
|
||||
handler: Handlers.servicesGet
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/deployment/{deploymentId}/service/{name}',
|
||||
method: 'put',
|
||||
config: {
|
||||
tags: ['api', 'deployment', 'service'],
|
||||
description: 'Perform an action on the named service',
|
||||
validate: {
|
||||
params: {
|
||||
deploymentId: Models.deploymentId,
|
||||
name: Models.serviceName
|
||||
},
|
||||
payload: Models.serviceUpdate
|
||||
},
|
||||
response: {
|
||||
schema: Models.service
|
||||
},
|
||||
handler: Handlers.serviceUpdate
|
||||
}
|
||||
}
|
||||
];
|
@ -27,6 +27,7 @@
|
||||
"graphi": "^2.0.0",
|
||||
"hoek": "^4.1.1",
|
||||
"joi": "^10.4.1",
|
||||
"joyent-cp-gql-schema": "^1.0.4",
|
||||
"portal-data": "^1.0.0"
|
||||
}
|
||||
}
|
||||
|
@ -32,315 +32,6 @@ describe('portal-api plugin', () => {
|
||||
});
|
||||
|
||||
|
||||
describe('deployments', () => {
|
||||
it('can be created', (done) => {
|
||||
const server = new Hapi.Server();
|
||||
server.connection();
|
||||
server.register(internals.register, (err) => {
|
||||
expect(err).to.not.exist();
|
||||
const payload = {
|
||||
name: 'User Services',
|
||||
datacenter: 'us-sw-1'
|
||||
};
|
||||
|
||||
server.inject({ method: 'POST', url: '/deployment', payload }, (res) => {
|
||||
expect(res.statusCode).to.equal(201);
|
||||
expect(res.headers.location).to.exist();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can be updated', (done) => {
|
||||
const server = new Hapi.Server();
|
||||
server.connection();
|
||||
server.register(internals.register, (err) => {
|
||||
expect(err).to.not.exist();
|
||||
const payload = {
|
||||
name: 'User Services',
|
||||
datacenter: 'us-sw-1'
|
||||
};
|
||||
|
||||
server.inject({ method: 'POST', url: '/deployment', payload }, (res) => {
|
||||
expect(res.statusCode).to.equal(201);
|
||||
expect(res.result.name).to.equal('User Services');
|
||||
payload.name = 'Customer Services';
|
||||
|
||||
server.inject({ method: 'PUT', url: `/deployment/${res.result.id}`, payload }, (res) => {
|
||||
expect(res.statusCode).to.equal(200);
|
||||
expect(res.result.name).to.equal('Customer Services');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can be retrieved', (done) => {
|
||||
const server = new Hapi.Server();
|
||||
server.connection();
|
||||
server.register(internals.register, (err) => {
|
||||
expect(err).to.not.exist();
|
||||
|
||||
const payload = {
|
||||
name: 'User Services',
|
||||
datacenter: 'us-sw-1'
|
||||
};
|
||||
|
||||
server.inject({ method: 'POST', url: '/deployment', payload }, (res) => {
|
||||
expect(res.statusCode).to.equal(201);
|
||||
expect(res.result.name).to.equal('User Services');
|
||||
|
||||
server.inject({ method: 'GET', url: `/deployment/${res.result.id}` }, (res) => {
|
||||
expect(res.statusCode).to.equal(200);
|
||||
expect(res.result.name).to.equal('User Services');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can be deleted', (done) => {
|
||||
const server = new Hapi.Server();
|
||||
server.connection();
|
||||
server.register(internals.register, (err) => {
|
||||
expect(err).to.not.exist();
|
||||
|
||||
const payload = {
|
||||
name: 'User Services',
|
||||
datacenter: 'us-sw-1'
|
||||
};
|
||||
|
||||
server.inject({ method: 'POST', url: '/deployment', payload }, (res) => {
|
||||
expect(res.statusCode).to.equal(201);
|
||||
expect(res.result.name).to.equal('User Services');
|
||||
|
||||
server.inject({ method: 'DELETE', url: `/deployment/${res.result.id}` }, (res) => {
|
||||
expect(res.statusCode).to.equal(200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can all be retrieved', (done) => {
|
||||
const server = new Hapi.Server();
|
||||
server.connection();
|
||||
server.register(internals.register, (err) => {
|
||||
expect(err).to.not.exist();
|
||||
|
||||
const deployment1 = {
|
||||
name: 'User Services',
|
||||
datacenter: 'us-sw-1'
|
||||
};
|
||||
|
||||
const deployment2 = {
|
||||
name: 'Customer Services',
|
||||
datacenter: 'us-sw-1'
|
||||
};
|
||||
|
||||
server.inject({ method: 'POST', url: '/deployment', payload: deployment1 }, (res) => {
|
||||
expect(res.statusCode).to.equal(201);
|
||||
expect(res.result.name).to.equal(deployment1.name);
|
||||
|
||||
server.inject({ method: 'POST', url: '/deployment', payload: deployment2 }, (res) => {
|
||||
expect(res.statusCode).to.equal(201);
|
||||
expect(res.result.name).to.equal(deployment2.name);
|
||||
|
||||
server.inject({ method: 'GET', url: '/deployments' }, (res) => {
|
||||
expect(res.statusCode).to.equal(200);
|
||||
expect(res.result.length >= 2).to.be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('datacenters', () => {
|
||||
it('can be retrieved', (done) => {
|
||||
const server = new Hapi.Server();
|
||||
server.connection();
|
||||
server.register(internals.register, (err) => {
|
||||
expect(err).to.not.exist();
|
||||
|
||||
server.inject({ method: 'GET', url: '/datacenters' }, (res) => {
|
||||
expect(res.statusCode).to.equal(200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('manifests', () => {
|
||||
it('can be created', (done) => {
|
||||
const server = new Hapi.Server();
|
||||
server.connection();
|
||||
server.register(internals.register, (err) => {
|
||||
expect(err).to.not.exist();
|
||||
const payload = {
|
||||
raw: 'blah',
|
||||
obj: {}
|
||||
};
|
||||
|
||||
const deployment = {
|
||||
name: 'User Services',
|
||||
datacenter: 'us-sw-1'
|
||||
};
|
||||
|
||||
server.inject({ method: 'POST', url: '/deployment', payload: deployment }, (res) => {
|
||||
expect(res.statusCode).to.equal(201);
|
||||
|
||||
server.inject({ method: 'POST', url: `/deployment/${res.result.id}/manifest`, payload }, (res) => {
|
||||
expect(res.statusCode).to.equal(201);
|
||||
expect(res.headers.location).to.exist();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can be retrieved', (done) => {
|
||||
const server = new Hapi.Server();
|
||||
server.connection();
|
||||
server.register(internals.register, (err) => {
|
||||
expect(err).to.not.exist();
|
||||
const payload = {
|
||||
raw: 'blah',
|
||||
obj: {}
|
||||
};
|
||||
|
||||
const deployment = {
|
||||
name: 'User Services',
|
||||
datacenter: 'us-sw-1'
|
||||
};
|
||||
|
||||
server.inject({ method: 'POST', url: '/deployment', payload: deployment }, (res) => {
|
||||
expect(res.statusCode).to.equal(201);
|
||||
|
||||
server.inject({ method: 'POST', url: `/deployment/${res.result.id}/manifest`, payload }, (res) => {
|
||||
expect(res.statusCode).to.equal(201);
|
||||
|
||||
server.inject(res.headers.location, (res) => {
|
||||
expect(res.statusCode).to.equal(200);
|
||||
expect(res.result.raw).to.equal(payload.raw);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('activities', () => {
|
||||
it('can be retrieved', (done) => {
|
||||
const server = new Hapi.Server();
|
||||
server.connection();
|
||||
server.register(internals.register, (err) => {
|
||||
expect(err).to.not.exist();
|
||||
|
||||
const deployment = {
|
||||
name: 'User Services',
|
||||
datacenter: 'us-sw-1'
|
||||
};
|
||||
|
||||
server.inject({ method: 'POST', url: '/deployment', payload: deployment }, (res) => {
|
||||
expect(res.statusCode).to.equal(201);
|
||||
|
||||
server.inject({ method: 'GET', url: `/deployment/${res.result.id}/activities` }, (res) => {
|
||||
expect(res.statusCode).to.equal(200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('metrics', () => {
|
||||
it.skip('can be retrieved', (done) => {
|
||||
const server = new Hapi.Server();
|
||||
server.connection();
|
||||
server.register(internals.register, (err) => {
|
||||
expect(err).to.not.exist();
|
||||
|
||||
const deployment = {
|
||||
name: 'User Services',
|
||||
datacenter: 'us-sw-1'
|
||||
};
|
||||
|
||||
server.inject({ method: 'POST', url: '/deployment', payload: deployment }, (res) => {
|
||||
expect(res.statusCode).to.equal(201);
|
||||
|
||||
server.inject({ method: 'GET', url: `/deployment/${res.result.id}/metrics` }, (res) => {
|
||||
expect(res.statusCode).to.equal(200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('services', () => {
|
||||
it('can be retrieved', (done) => {
|
||||
const server = new Hapi.Server();
|
||||
server.connection();
|
||||
server.register(internals.register, (err) => {
|
||||
expect(err).to.not.exist();
|
||||
|
||||
const deployment = {
|
||||
name: 'User Services',
|
||||
datacenter: 'us-sw-1'
|
||||
};
|
||||
|
||||
server.inject({ method: 'POST', url: '/deployment', payload: deployment }, (res) => {
|
||||
expect(res.statusCode).to.equal(201);
|
||||
|
||||
server.inject({ method: 'GET', url: `/deployment/${res.result.id}/services` }, (res) => {
|
||||
expect(res.statusCode).to.equal(200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can be updated', (done) => {
|
||||
const server = new Hapi.Server();
|
||||
server.connection();
|
||||
server.register(internals.register, (err) => {
|
||||
expect(err).to.not.exist();
|
||||
|
||||
const deployment = {
|
||||
name: 'User Services',
|
||||
datacenter: 'us-sw-1'
|
||||
};
|
||||
|
||||
server.inject({ method: 'POST', url: '/deployment', payload: deployment }, (res) => {
|
||||
expect(res.statusCode).to.equal(201);
|
||||
const deploymentId = res.result.id;
|
||||
|
||||
server.inject({ method: 'GET', url: `/deployment/${deploymentId}/services` }, (res) => {
|
||||
expect(res.statusCode).to.equal(200);
|
||||
|
||||
const service = {
|
||||
count: 2
|
||||
};
|
||||
|
||||
server.inject({ method: 'PUT', url: `/deployment/${deploymentId}/service/consul`, payload: service }, (res) => {
|
||||
expect(res.statusCode).to.equal(200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('graphql', () => {
|
||||
it('route exists', (done) => {
|
||||
const server = new Hapi.Server();
|
||||
|
42
packages/portal-data/.yarnclean
Normal file
42
packages/portal-data/.yarnclean
Normal file
@ -0,0 +1,42 @@
|
||||
# test directories
|
||||
__tests__
|
||||
test
|
||||
tests
|
||||
powered-test
|
||||
|
||||
# asset directories
|
||||
docs
|
||||
doc
|
||||
website
|
||||
images
|
||||
assets
|
||||
|
||||
# examples
|
||||
example
|
||||
examples
|
||||
|
||||
# code coverage directories
|
||||
coverage
|
||||
.nyc_output
|
||||
|
||||
# build scripts
|
||||
Makefile
|
||||
Gulpfile.js
|
||||
Gruntfile.js
|
||||
|
||||
# configs
|
||||
.tern-project
|
||||
.gitattributes
|
||||
.editorconfig
|
||||
.*ignore
|
||||
.eslintrc
|
||||
.jshintrc
|
||||
.flowconfig
|
||||
.documentup.json
|
||||
.yarn-metadata.json
|
||||
.*.yml
|
||||
*.yml
|
||||
|
||||
# misc
|
||||
*.gz
|
||||
*.md
|
@ -3,7 +3,9 @@
|
||||
const Hoek = require('hoek');
|
||||
const Penseur = require('penseur');
|
||||
const DCClient = require('docker-compose-client');
|
||||
const Awaitify = require('apr-awaitify');
|
||||
const VAsync = require('vasync');
|
||||
const Transform = require('./transform');
|
||||
|
||||
|
||||
const internals = {
|
||||
defaults: {
|
||||
@ -13,13 +15,16 @@ const internals = {
|
||||
},
|
||||
dockerHost: 'tcp://0.0.0.0:4242'
|
||||
},
|
||||
tables: [
|
||||
'activities',
|
||||
'datacenters',
|
||||
'deployments',
|
||||
'manifests',
|
||||
'metrics'
|
||||
]
|
||||
tables: {
|
||||
'portals': { id: { type: 'uuid' } },
|
||||
'datacenters': { id: { type: 'uuid' } },
|
||||
'deployment_groups': { id: { type: 'uuid' } },
|
||||
'versions': { id: { type: 'uuid' } },
|
||||
'manifests': { id: { type: 'uuid' } },
|
||||
'services': { id: { type: 'uuid' } },
|
||||
'packages': { id: { type: 'uuid' } },
|
||||
'instances': { id: { type: 'uuid' } }
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = class Data {
|
||||
@ -31,38 +36,83 @@ module.exports = class Data {
|
||||
this._docker = new DCClient(settings.dockerHost);
|
||||
}
|
||||
|
||||
connect () {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._db.establish(internals.tables, (err) => {
|
||||
connect (cb) {
|
||||
this._db.establish(internals.tables, cb);
|
||||
}
|
||||
|
||||
|
||||
// portals
|
||||
|
||||
createPortal (clientPortal, cb) {
|
||||
const portal = Transform.toPortal(clientPortal);
|
||||
this._db.portals.insert(portal, (err, key) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
portal.id = key;
|
||||
cb(null, Transform.fromPortal({ portal }));
|
||||
});
|
||||
}
|
||||
|
||||
getPortal (cb) {
|
||||
this._db.portals.all((err, portals) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
const portal = portals[0];
|
||||
VAsync.parallel({
|
||||
funcs: [
|
||||
(next) => {
|
||||
this.getDatacenter({ id: portal.datacenter_id }, next);
|
||||
},
|
||||
(next) => {
|
||||
this.getDeploymentGroups(portal.deployment_group_ids, next);
|
||||
}
|
||||
]
|
||||
}, (err, results) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
// promisify Penseur
|
||||
internals.tables.forEach((tableName) => {
|
||||
return ['insert', 'get', 'update', 'remove', 'all'].forEach((methodName) => {
|
||||
this._db[tableName][methodName] = Awaitify(
|
||||
this._db[tableName][methodName]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
resolve();
|
||||
cb(null, Transform.fromPortal({ portal, datacenter: results.successes[0], deploymentGroups: results.successes[1] }));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* DeploymentGroupUuid
|
||||
* Manifest
|
||||
* id: UUID,
|
||||
* created: Date.now(),
|
||||
* type: 'docker-compose',
|
||||
* format: 'yml',
|
||||
* raw: 'original yml file content',
|
||||
* obj: { }
|
||||
*/
|
||||
createDeployment ({ deploymentGroupUuid, manifest, deployment }) {
|
||||
|
||||
// datacenters
|
||||
|
||||
createDatacenter (datacenter, cb) {
|
||||
this._db.datacenters.insert(datacenter, (err, key) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
datacenter.id = key;
|
||||
cb(null, datacenter);
|
||||
});
|
||||
}
|
||||
|
||||
getDatacenters (cb) {
|
||||
this._db.datacenters.all(cb);
|
||||
}
|
||||
|
||||
getDatacenter ({ id, region }, cb) {
|
||||
Hoek.assert(id || region, 'id or region are required to retrieve a datacenter');
|
||||
|
||||
if (region) {
|
||||
return this._db.datacenters.single({ region }, cb);
|
||||
}
|
||||
|
||||
this._db.datacenters.single({ id }, cb);
|
||||
}
|
||||
|
||||
|
||||
// deployment_groups
|
||||
|
||||
createDeploymentGroup ({ name }, cb) {
|
||||
// trigger deployment
|
||||
// create deployment queue (we should think about what is a deployment queue)
|
||||
// create the ConvergencePlans
|
||||
@ -70,111 +120,68 @@ module.exports = class Data {
|
||||
// create a Version
|
||||
// update the DeploymentGroup
|
||||
|
||||
// TODO
|
||||
const updateDb = (plan) => {
|
||||
// deployment.services = [];
|
||||
// deployment.state = { current: 'stopped' };
|
||||
|
||||
this._db.deployments
|
||||
.insert({
|
||||
name: deployment.name
|
||||
})
|
||||
.then((key) => {
|
||||
deployment.id = key;
|
||||
return deployment;
|
||||
});
|
||||
};
|
||||
|
||||
const provision = ({ name }) => {
|
||||
return this._docker
|
||||
.provision({
|
||||
projectName: name,
|
||||
manifest: manifest.raw
|
||||
})
|
||||
.then(updateDb);
|
||||
};
|
||||
|
||||
this.getDeployment(deploymentGroupUuid).then(provision);
|
||||
}
|
||||
|
||||
getDeployment (id) {
|
||||
return this._db.deployments.get(id);
|
||||
}
|
||||
|
||||
updateDeployment (deployment) {
|
||||
return this._db.deployments.update(deployment.id, deployment);
|
||||
}
|
||||
|
||||
deleteDeployment (id) {
|
||||
return this._db.deployments.remove(id);
|
||||
}
|
||||
|
||||
getDeployments () {
|
||||
return this._db.deployments.all();
|
||||
}
|
||||
|
||||
getDatacenters () {
|
||||
return this._db.datacenters.all();
|
||||
}
|
||||
|
||||
createManifest (deploymentId, manifest) {
|
||||
manifest.deploymentId = deploymentId;
|
||||
manifest.created = Date.now();
|
||||
|
||||
return this._db.manifests.insert().then((id) => {
|
||||
manifest.id = id;
|
||||
return manifest;
|
||||
});
|
||||
}
|
||||
getManifest (id) {
|
||||
return this._db.manifests.get();
|
||||
}
|
||||
|
||||
getActivities (deploymentId) {
|
||||
return this._db.activities.query({ deploymentId });
|
||||
}
|
||||
|
||||
getMetrics (containerId) {
|
||||
return this._db.metrics.get(containerId);
|
||||
}
|
||||
|
||||
insertMetrics (containerId, metrics) {
|
||||
return this._db.metrics.get(containerId).then((existing) => {
|
||||
if (existing) {
|
||||
return this._db.metrics.update(containerId, {
|
||||
metrics: this._db.append(metrics)
|
||||
});
|
||||
this._db.deployment_groups.insert({ name }, (err, key) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
const entry = { id: containerId, metrics };
|
||||
return this._db.metrics.insert(entry, { merge: true });
|
||||
cb(null, Transform.fromDeploymentGroup({ id: key, name }));
|
||||
});
|
||||
}
|
||||
|
||||
getServices (deploymentId) {
|
||||
this._db.deployments.get(deploymentId, { filter: 'services' });
|
||||
}
|
||||
|
||||
updateService (deploymentId, service) {
|
||||
this._db.deployments.get(deploymentId, { filter: 'services' }).then((deployment) => {
|
||||
const serviceToUpdate = deployment.services.find((currentService) => {
|
||||
return currentService.name === service.name;
|
||||
});
|
||||
|
||||
if (!serviceToUpdate) {
|
||||
deployment.services.push(service);
|
||||
} else {
|
||||
serviceToUpdate.count = service.count;
|
||||
serviceToUpdate.containers = service.containers;
|
||||
updateDeploymentGroup ({ id, name }, cb) {
|
||||
this._db.deployment_groups.update(id, { name }, (err) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
return this._db.deployments.update(deploymentId, {
|
||||
services: deployment.services
|
||||
});
|
||||
cb(null, Transform.fromDeploymentGroup({ id, name }));
|
||||
});
|
||||
}
|
||||
|
||||
deploymentChanges (handler) {
|
||||
return this._db.deployments.changes('*', { reconnect: true, handler });
|
||||
getDeploymentGroups (ids, cb) {
|
||||
this._db.deployment_groups.get(ids, (err, deploymentGroups) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
deploymentGroups = deploymentGroups || [];
|
||||
cb(null, deploymentGroups.map(Transform.fromDeploymentGroup));
|
||||
});
|
||||
}
|
||||
|
||||
getDeploymentGroup (id, cb) {
|
||||
this._db.deployment_groups.single({ id }, (err, deploymentGroup) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
cb(null, Transform.fromDeploymentGroup(deploymentGroup || {}));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// versions
|
||||
|
||||
createVersion (clientVersion, cb) {
|
||||
const version = Transform.toVersion(clientVersion);
|
||||
this._db.versions.insert(version, (err, key) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
version.id = key;
|
||||
cb(null, Transform.fromVersion(version));
|
||||
});
|
||||
}
|
||||
|
||||
getVersion (id, cb) {
|
||||
this._db.versions.single({ id }, (err, version) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
cb(null, Transform.fromVersion(version));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
94
packages/portal-data/lib/transform.js
Normal file
94
packages/portal-data/lib/transform.js
Normal file
@ -0,0 +1,94 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
exports.fromPortal = function ({ portal, datacenter, deploymentGroups }) {
|
||||
deploymentGroups = Array.isArray(deploymentGroups) ? deploymentGroups : [];
|
||||
|
||||
return {
|
||||
id: portal.id,
|
||||
username: portal.username,
|
||||
datacenter,
|
||||
deploymentGroups: deploymentGroups.map(exports.fromDeploymentGroup)
|
||||
};
|
||||
};
|
||||
|
||||
exports.toPortal = function (clientPortal) {
|
||||
return {
|
||||
username: clientPortal.username,
|
||||
datacenter_id: clientPortal.datacenter ? clientPortal.datacenter.id : '',
|
||||
deployment_group_ids: clientPortal.deploymentGroups ? clientPortal.deploymentGroups.map((deploymentGroup) => {
|
||||
return deploymentGroup.id;
|
||||
}) : []
|
||||
};
|
||||
};
|
||||
|
||||
exports.fromDeploymentGroup = function (deploymentGroup, services) {
|
||||
if (!Array.isArray(services)) {
|
||||
services = [];
|
||||
}
|
||||
|
||||
return {
|
||||
id: deploymentGroup.id,
|
||||
name: deploymentGroup.name,
|
||||
slug: deploymentGroup.slug,
|
||||
services: services.map(exports.fromService),
|
||||
version: deploymentGroup.version_id,
|
||||
history: deploymentGroup.history_version_ids || []
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
exports.fromService = function (service) {
|
||||
|
||||
};
|
||||
|
||||
|
||||
exports.toVersion = function (clientVersion) {
|
||||
return {
|
||||
id: clientVersion.id,
|
||||
created: clientVersion.created || Date.now(),
|
||||
manifest_id: clientVersion.manifestId,
|
||||
service_scales: clientVersion.scales ? clientVersion.scales.map(exports.toScale) : [],
|
||||
plan: exports.toPlan(clientVersion.plan || {})
|
||||
};
|
||||
};
|
||||
|
||||
exports.fromVersion = function (version) {
|
||||
return {
|
||||
id: version.id,
|
||||
created: version.created,
|
||||
manifestId: version.manifest_id,
|
||||
scales: 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
|
||||
};
|
||||
};
|
@ -6,7 +6,6 @@
|
||||
"scripts": {
|
||||
"bootstrap": "node ./bootstrap-data",
|
||||
"lint": "belly-button",
|
||||
"fmt": "prettier --write --single-quote {lib,test}/**/*.js",
|
||||
"rethinkdb-up": "docker run -d -p 8080:8080 -p 28015:28015 -p 29015:29015 --name rethinkdb rethinkdb",
|
||||
"rethinkdb-down": "docker rm -f rethinkdb",
|
||||
"test": "exit 0 # npm run lint && lab -t 40"
|
||||
@ -15,10 +14,10 @@
|
||||
"author": "wyatt",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"apr-awaitify": "^1.0.4",
|
||||
"docker-compose-client": "^1.0.3",
|
||||
"hoek": "^4.1.1",
|
||||
"penseur": "^7.8.1"
|
||||
"penseur": "^7.8.1",
|
||||
"vasync": "^1.6.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"belly-button": "^3.1.0",
|
||||
|
@ -12,208 +12,252 @@ const internals = {
|
||||
options: { name: 'test', db: { test: true } }
|
||||
};
|
||||
|
||||
describe('connect()', function () {
|
||||
it('connects to the database', () => {
|
||||
describe('connect()', () => {
|
||||
it('connects to the database', (done) => {
|
||||
const data = new PortalData(internals.options);
|
||||
|
||||
return data.connect();
|
||||
data.connect(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('createDeployment()', () => {
|
||||
it('creates a deployment record in the deployment table', (done) => {
|
||||
const data = new PortalData(internals.options);
|
||||
const deployment = {
|
||||
name: 'User Services',
|
||||
datacenter: 'us-sw-1'
|
||||
};
|
||||
|
||||
data
|
||||
.connect()
|
||||
.then(() => {
|
||||
data
|
||||
.createDeployment({
|
||||
deployment
|
||||
})
|
||||
.then((deployment) => {
|
||||
expect(deployment.id).to.exist();
|
||||
done();
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
describe('portals', () => {
|
||||
describe('createPortal()', () => {
|
||||
it('creates a new portal', (done) => {
|
||||
const data = new PortalData(internals.options);
|
||||
data.connect((err) => {
|
||||
expect(err).to.not.exist();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('getDeployment()', () => {
|
||||
it('will retrieve an existing deployment', (done) => {
|
||||
const data = new PortalData(internals.options);
|
||||
data.connect().then(() => {
|
||||
const deployment = {
|
||||
name: 'User Services',
|
||||
datacenter: 'us-sw-1'
|
||||
};
|
||||
const portal = {
|
||||
username: 'tom'
|
||||
};
|
||||
|
||||
data.createDeployment(deployment).then((deployment) => {
|
||||
expect(deployment.id).to.exist();
|
||||
data.getDeployment(deployment.id).then((retrievedDeployment) => {
|
||||
expect(deployment).to.equal(retrievedDeployment);
|
||||
data.createPortal(portal, (err, result) => {
|
||||
expect(err).to.not.exist();
|
||||
expect(result.id).to.exist();
|
||||
expect(result.username).to.equal(portal.username);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('updateService()', () => {
|
||||
it('will update the services for an existing deployment', (done) => {
|
||||
const data = new PortalData(internals.options);
|
||||
data.connect().then(() => {
|
||||
const deployment = {
|
||||
name: 'User Services',
|
||||
datacenter: 'us-sw-1'
|
||||
};
|
||||
const service = {
|
||||
name: 'consul',
|
||||
containers: [
|
||||
{
|
||||
server_id: '423e7432-b760-11e2-bf6c-002590c3f1a0',
|
||||
alias: 'nodejsexample_consul_1',
|
||||
image_id: '91b757b5-bd29-2126-5ff9-ae9235011ff5',
|
||||
owner_id: '30f62ec2-24a2-6f8e-8fad-d46b04c8a0b9',
|
||||
id: '81205d4a-92f4-c4d9-da8a-aafd689eeabb'
|
||||
}
|
||||
],
|
||||
count: 1
|
||||
};
|
||||
describe('getPortal()', () => {
|
||||
it('retrieves a single portal record', (done) => {
|
||||
const data = new PortalData(internals.options);
|
||||
data.connect((err) => {
|
||||
expect(err).to.not.exist();
|
||||
const datacenter = {
|
||||
region: 'us-sw-1'
|
||||
};
|
||||
|
||||
data.createDeployment(deployment).then((deployment) => {
|
||||
expect(deployment.id).to.exist();
|
||||
data.updateService(deployment.id, service).then((updatedService) => {
|
||||
expect(updatedService).to.equal(service);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
data.createDatacenter(datacenter, (err, createdDatacenter) => {
|
||||
expect(err).to.not.exist();
|
||||
const portal = {
|
||||
username: 'tom',
|
||||
datacenter: {
|
||||
id: createdDatacenter.id
|
||||
}
|
||||
};
|
||||
|
||||
describe.skip('deploymentChanges()', () => {
|
||||
it('will execute the handler when a deployment service changes', (done) => {
|
||||
const data = new PortalData(internals.options);
|
||||
data.connect().then(() => {
|
||||
const deployment = {
|
||||
name: 'User Services',
|
||||
datacenter: 'us-sw-1'
|
||||
};
|
||||
const service1 = {
|
||||
name: 'consul',
|
||||
containers: [
|
||||
{
|
||||
server_id: '423e7432-b760-11e2-bf6c-002590c3f1a0',
|
||||
alias: 'nodejsexample_consul_1',
|
||||
image_id: '91b757b5-bd29-2126-5ff9-ae9235011ff5',
|
||||
owner_id: '30f62ec2-24a2-6f8e-8fad-d46b04c8a0b9',
|
||||
id: '81205d4a-92f4-c4d9-da8a-aafd689eeabb'
|
||||
}
|
||||
],
|
||||
count: 1
|
||||
};
|
||||
|
||||
const service2 = {
|
||||
name: 'consul',
|
||||
containers: [
|
||||
{
|
||||
server_id: '423e7432-b760-11e2-bf6c-002590c3f1a0',
|
||||
alias: 'nodejsexample_consul_1',
|
||||
image_id: '91b757b5-bd29-2126-5ff9-ae9235011ff5',
|
||||
owner_id: '30f62ec2-24a2-6f8e-8fad-d46b04c8a0b9',
|
||||
id: '81205d4a-92f4-c4d9-da8a-aafd689eeabb'
|
||||
},
|
||||
{
|
||||
server_id: '423e7432-b760-11e2-bf6c-002590c3f1a0',
|
||||
alias: 'nodejsexample_consul_2',
|
||||
image_id: '91b757b5-bd29-2126-5ff9-ae9235011ff5',
|
||||
owner_id: '30f62ec2-24a2-6f8e-8fad-d46b04c8a0b9',
|
||||
id: '81205d4a-92f4-c4d9-da8a-aafd689eeabb'
|
||||
},
|
||||
{
|
||||
server_id: '423e7432-b760-11e2-bf6c-002590c3f1a0',
|
||||
alias: 'nodejsexample_consul_3',
|
||||
image_id: '91b757b5-bd29-2126-5ff9-ae9235011ff5',
|
||||
owner_id: '30f62ec2-24a2-6f8e-8fad-d46b04c8a0b9',
|
||||
id: '81205d4a-92f4-c4d9-da8a-aafd689eeabb'
|
||||
}
|
||||
],
|
||||
count: 3
|
||||
};
|
||||
|
||||
data.createDeployment(deployment).then((deployment) => {
|
||||
expect(deployment.id).to.exist();
|
||||
data.updateService(deployment.id, service1).then((updatedService1) => {
|
||||
expect(updatedService1).to.equal(service1);
|
||||
|
||||
let executed = false;
|
||||
data
|
||||
.deploymentChanges((err, changes) => {
|
||||
data.createPortal(portal, (err, createdPortal) => {
|
||||
expect(err).to.not.exist();
|
||||
expect(createdPortal.id).to.exist();
|
||||
data.getPortal((err, retrievedPortal) => {
|
||||
expect(err).to.not.exist();
|
||||
if (executed) {
|
||||
return;
|
||||
}
|
||||
|
||||
expect(changes.before).to.exist();
|
||||
expect(changes.after).to.exist();
|
||||
expect(retrievedPortal.id).to.exist();
|
||||
expect(retrievedPortal.username).to.equal(portal.username);
|
||||
done();
|
||||
executed = true;
|
||||
})
|
||||
.then(() => {
|
||||
data
|
||||
.updateService(deployment.id, service2)
|
||||
.then((updatedService2) => {
|
||||
expect(updatedService2).to.equal(service2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('insertMetrics()', () => {
|
||||
it("will add new metrics to a service and won't overwrite existing ones", (done) => {
|
||||
const data = new PortalData(internals.options);
|
||||
data.connect().then(() => {
|
||||
const containerId = '81205d4a-92f4-c4d9-da8a-aafd689eeabb';
|
||||
const metrics1 = [
|
||||
{
|
||||
timestamp: 1494360995851,
|
||||
cpu: 1.2,
|
||||
memory: 23344523,
|
||||
network: 5024
|
||||
}
|
||||
];
|
||||
|
||||
const metrics2 = [
|
||||
{
|
||||
timestamp: 1495360995851,
|
||||
cpu: 1.3,
|
||||
memory: 23344523,
|
||||
network: 4024
|
||||
}
|
||||
];
|
||||
|
||||
data.insertMetrics(containerId, metrics1).then((result1) => {
|
||||
expect(result1.id).to.equal(containerId);
|
||||
expect(result1.metrics).to.equal(metrics1);
|
||||
data.insertMetrics(containerId, metrics2).then((result2) => {
|
||||
expect(result2.id).to.equal(containerId);
|
||||
data.getMetrics(containerId).then((results) => {
|
||||
expect(results.metrics.length).to.equal(2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('deployment groups', () => {
|
||||
describe('createDeploymentGroup()', () => {
|
||||
it('creates a deployment group record in the deployment_groups table', (done) => {
|
||||
const data = new PortalData(internals.options);
|
||||
const name = 'User Services';
|
||||
|
||||
data.connect((err) => {
|
||||
expect(err).to.not.exist();
|
||||
data.createDeploymentGroup({ name }, (err, deploymentGroup) => {
|
||||
expect(err).to.not.exist();
|
||||
expect(deploymentGroup.id).to.exist();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDeploymentGroup()', () => {
|
||||
it('gets a deployment group record from the deployment_groups table', (done) => {
|
||||
const data = new PortalData(internals.options);
|
||||
const name = 'User Services';
|
||||
|
||||
data.connect((err) => {
|
||||
expect(err).to.not.exist();
|
||||
data.createDeploymentGroup({ name }, (err, createdDeploymentGroup) => {
|
||||
expect(err).to.not.exist();
|
||||
expect(createdDeploymentGroup.id).to.exist();
|
||||
data.getDeploymentGroup(createdDeploymentGroup.id, (err, deploymentGroup) => {
|
||||
expect(err).to.not.exist();
|
||||
expect(deploymentGroup).to.equal(createdDeploymentGroup);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDeploymentGroups()', () => {
|
||||
it('gets a list of deployment group records from the deployment_groups table', (done) => {
|
||||
const data = new PortalData(internals.options);
|
||||
const name = 'User Services';
|
||||
|
||||
data.connect((err) => {
|
||||
expect(err).to.not.exist();
|
||||
data.createDeploymentGroup({ name }, (err, createdDeploymentGroup1) => {
|
||||
expect(err).to.not.exist();
|
||||
expect(createdDeploymentGroup1.id).to.exist();
|
||||
data.createDeploymentGroup({ name }, (err, createdDeploymentGroup2) => {
|
||||
expect(err).to.not.exist();
|
||||
expect(createdDeploymentGroup1.id).to.exist();
|
||||
|
||||
data.getDeploymentGroups([createdDeploymentGroup1.id, createdDeploymentGroup2.id], (err, deploymentGroups) => {
|
||||
expect(err).to.not.exist();
|
||||
expect(deploymentGroups.length).to.equal(2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('datacenters', () => {
|
||||
describe('createDatacenter()', () => {
|
||||
it('creates a new datacenter record', (done) => {
|
||||
const data = new PortalData(internals.options);
|
||||
data.connect((err) => {
|
||||
expect(err).to.not.exist();
|
||||
const datacenter = {
|
||||
region: 'us-sw-1'
|
||||
};
|
||||
|
||||
data.createDatacenter(datacenter, (err, result) => {
|
||||
expect(err).to.not.exist();
|
||||
expect(result.id).to.exist();
|
||||
expect(result.region).to.equal(datacenter.region);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDatacenter()', () => {
|
||||
it('retrieves a datacenter record from an id', (done) => {
|
||||
const data = new PortalData(internals.options);
|
||||
data.connect((err) => {
|
||||
expect(err).to.not.exist();
|
||||
const datacenter = {
|
||||
region: 'us-sw-1'
|
||||
};
|
||||
|
||||
data.createDatacenter(datacenter, (err, createdDatacenter) => {
|
||||
expect(err).to.not.exist();
|
||||
expect(createdDatacenter.id).to.exist();
|
||||
data.getDatacenter({ id: createdDatacenter.id }, (err, retrievedDatacenter) => {
|
||||
expect(err).to.not.exist();
|
||||
expect(retrievedDatacenter.region).to.equal(datacenter.region);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('retrieves a datacenter record from a region', (done) => {
|
||||
const data = new PortalData(internals.options);
|
||||
data.connect((err) => {
|
||||
expect(err).to.not.exist();
|
||||
const datacenter = {
|
||||
region: 'us-sw-1'
|
||||
};
|
||||
|
||||
data.createDatacenter(datacenter, (err, createdDatacenter) => {
|
||||
expect(err).to.not.exist();
|
||||
expect(createdDatacenter.id).to.exist();
|
||||
data.getDatacenter({ region: createdDatacenter.region }, (err, retrievedDatacenter) => {
|
||||
expect(err).to.not.exist();
|
||||
expect(retrievedDatacenter.region).to.equal(datacenter.region);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDatacenters()', () => {
|
||||
it('retrieves all datacenter records', (done) => {
|
||||
const data = new PortalData(internals.options);
|
||||
data.connect((err) => {
|
||||
expect(err).to.not.exist();
|
||||
const datacenter1 = {
|
||||
region: 'us-sw-1'
|
||||
};
|
||||
|
||||
const datacenter2 = {
|
||||
region: 'us-west-1'
|
||||
};
|
||||
|
||||
data.createDatacenter(datacenter1, (err, createdDatacenter1) => {
|
||||
expect(err).to.not.exist();
|
||||
data.createDatacenter(datacenter2, (err, createdDatacenter2) => {
|
||||
expect(err).to.not.exist();
|
||||
data.getDatacenters((err, datacenters) => {
|
||||
expect(err).to.not.exist();
|
||||
expect(datacenters.length).to.equal(2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('versions', () => {
|
||||
describe('createVersion()', () => {
|
||||
it('creates a new version record in the versions table', (done) => {
|
||||
const data = new PortalData(internals.options);
|
||||
data.connect((err) => {
|
||||
expect(err).to.not.exist();
|
||||
const clientVersion = {
|
||||
manifestId: 'something',
|
||||
scales: [{
|
||||
serviceName: 'consul',
|
||||
replicas: 3
|
||||
}],
|
||||
plan: {
|
||||
running: true,
|
||||
actions: [{
|
||||
type: 'start',
|
||||
service: 'consul',
|
||||
machines: ['vmid', 'vmid']
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
data.createVersion(clientVersion, (err, result) => {
|
||||
expect(err).to.not.exist();
|
||||
expect(result.id).to.exist();
|
||||
expect(result.scales).to.equal(clientVersion.scales);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -276,6 +276,13 @@ 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.3:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/docker-compose-client/-/docker-compose-client-1.0.7.tgz#a2f351aff998fd5323b9b6bb27d4400fff95e43c"
|
||||
dependencies:
|
||||
apr-awaitify "^1.0.4"
|
||||
zerorpc "^0.9.7"
|
||||
|
||||
doctrine@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63"
|
||||
@ -448,6 +455,10 @@ exit-hook@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
|
||||
|
||||
extsprintf@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.2.0.tgz#5ad946c22f5b32ba7f8cd7426711c6e8a3fc2529"
|
||||
|
||||
fast-levenshtein@~2.0.4:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
||||
@ -1183,6 +1194,18 @@ uuid@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"
|
||||
|
||||
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.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/verror/-/verror-1.6.0.tgz#7d13b27b1facc2e2da90405eb5ea6e5bdd252ea5"
|
||||
dependencies:
|
||||
extsprintf "1.2.0"
|
||||
|
||||
window-size@0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
|
||||
|
Loading…
Reference in New Issue
Block a user