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": "^7.2.0",
|
||||||
"good-console": "^6.4.0",
|
"good-console": "^6.4.0",
|
||||||
"good-squeeze": "^5.0.2",
|
"good-squeeze": "^5.0.2",
|
||||||
"graphql": "^0.9.6",
|
"graphi": "^2.0.0",
|
||||||
"graphql-server-hapi": "^0.7.2",
|
|
||||||
"graphql-tools": "^0.11.0",
|
|
||||||
"hapi": "^16.1.1",
|
"hapi": "^16.1.1",
|
||||||
"joi": "^10.5.0",
|
"joi": "^10.5.0",
|
||||||
"joyent-cp-gql-schema": "^1.0.4"
|
"joyent-cp-gql-schema": "^1.0.4"
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
const schema = require('joyent-cp-gql-schema');
|
const schema = require('joyent-cp-gql-schema');
|
||||||
const { graphqlHapi, graphiqlHapi } = require('graphql-server-hapi');
|
const graphi = require('graphi');
|
||||||
const { makeExecutableSchema } = require('graphql-tools');
|
|
||||||
const Good = require('good');
|
const Good = require('good');
|
||||||
const Hapi = require('hapi');
|
const Hapi = require('hapi');
|
||||||
const resolvers = require('./resolvers');
|
const resolvers = require('./resolvers');
|
||||||
@ -45,27 +44,12 @@ server.register(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
register: graphiqlHapi,
|
register: graphi,
|
||||||
options: {
|
options: {
|
||||||
path: '/graphiql',
|
graphqlPath: '/graphql',
|
||||||
graphiqlOptions: {
|
graphiqlPath: '/graphiql',
|
||||||
endpointURL: '/graphql'
|
schema,
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
register: graphqlHapi,
|
|
||||||
options: {
|
|
||||||
path: '/graphql',
|
|
||||||
graphqlOptions: {
|
|
||||||
schema: makeExecutableSchema({
|
|
||||||
typeDefs: schema.sync(),
|
|
||||||
resolvers
|
resolvers
|
||||||
})
|
|
||||||
},
|
|
||||||
route: {
|
|
||||||
cors: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -2,60 +2,10 @@
|
|||||||
# yarn lockfile v1
|
# 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":
|
"@types/graphql@^0.9.0":
|
||||||
version "0.9.1"
|
version "0.9.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.9.1.tgz#b04ebe84bc997cc60dbea2ed4d0d4342c737f99d"
|
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:
|
abbrev@1:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f"
|
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f"
|
||||||
@ -229,7 +179,7 @@ boom@2.x.x:
|
|||||||
dependencies:
|
dependencies:
|
||||||
hoek "2.x.x"
|
hoek "2.x.x"
|
||||||
|
|
||||||
boom@4.x.x, boom@^4.3.1:
|
boom@4.x.x:
|
||||||
version "4.3.1"
|
version "4.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31"
|
resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -439,10 +389,6 @@ delegates@^1.0.0:
|
|||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
|
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:
|
doctrine@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63"
|
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"
|
version "4.1.11"
|
||||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
|
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"
|
version "0.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/graphql-server-core/-/graphql-server-core-0.7.0.tgz#a658b3b0704f8a19b24254b64bef6b3d0ddce8ba"
|
resolved "https://registry.yarnpkg.com/graphql-server-core/-/graphql-server-core-0.7.0.tgz#a658b3b0704f8a19b24254b64bef6b3d0ddce8ba"
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
"@types/graphql" "^0.9.0"
|
"@types/graphql" "^0.9.0"
|
||||||
|
|
||||||
graphql-server-hapi@^0.7.2:
|
graphql-server-module-graphiql@0.7.x:
|
||||||
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:
|
|
||||||
version "0.7.2"
|
version "0.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/graphql-server-module-graphiql/-/graphql-server-module-graphiql-0.7.2.tgz#aa1f2a26eadbf7127c1b077e633d5086da52b330"
|
resolved "https://registry.yarnpkg.com/graphql-server-module-graphiql/-/graphql-server-module-graphiql-0.7.2.tgz#aa1f2a26eadbf7127c1b077e633d5086da52b330"
|
||||||
|
|
||||||
graphql-tools@^0.11.0:
|
graphql@0.9.x:
|
||||||
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:
|
|
||||||
version "0.9.6"
|
version "0.9.6"
|
||||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.9.6.tgz#514421e9d225c29dfc8fd305459abae58815ef2c"
|
resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.9.6.tgz#514421e9d225c29dfc8fd305459abae58815ef2c"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2149,7 +2083,7 @@ uuid@^2.0.1:
|
|||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
|
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"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { readFile } = require('mz/fs');
|
|
||||||
const { readFileSync } = require('fs');
|
const { readFileSync } = require('fs');
|
||||||
|
|
||||||
const file = path.join(__dirname, 'schema.gql');
|
const file = path.join(__dirname, 'schema.gql');
|
||||||
|
|
||||||
module.exports = () => readFile(file, 'utf-8');
|
module.exports = readFileSync(file, 'utf-8');
|
||||||
module.exports.sync = () => readFileSync(file, 'utf-8');
|
|
||||||
|
@ -9,9 +9,7 @@
|
|||||||
"lint": "eslint . --fix --format=tap",
|
"lint": "eslint . --fix --format=tap",
|
||||||
"test": "exit 0"
|
"test": "exit 0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {},
|
||||||
"mz": "^2.6.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^3.19.0",
|
"eslint": "^3.19.0",
|
||||||
"eslint-config-joyent-portal": "1.0.0"
|
"eslint-config-joyent-portal": "1.0.0"
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
type User {
|
type User {
|
||||||
uuid: ID!
|
id: ID!
|
||||||
firstName: String!
|
firstName: String!
|
||||||
lastName: String!
|
lastName: String!
|
||||||
email: String!
|
email: String!
|
||||||
@ -17,7 +17,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DeploymentGroup {
|
type DeploymentGroup {
|
||||||
uuid: ID!
|
id: ID!
|
||||||
name: String!
|
name: String!
|
||||||
slug: String!
|
slug: String!
|
||||||
services(slug: String): [Service]!
|
services(slug: String): [Service]!
|
||||||
@ -26,7 +26,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ServiceScale {
|
type ServiceScale {
|
||||||
uuid: ID!
|
id: ID!
|
||||||
serviceName: String!
|
serviceName: String!
|
||||||
replicas: Int!
|
replicas: Int!
|
||||||
}
|
}
|
||||||
@ -39,14 +39,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ConvergenceAction {
|
type ConvergenceAction {
|
||||||
uuid: String!
|
id: String!
|
||||||
type: ConvergenceActionType!
|
type: ConvergenceActionType!
|
||||||
service: String! # service name
|
service: String! # service name
|
||||||
machines: [String]! # instance machine ids
|
machines: [String]! # instance machine ids
|
||||||
}
|
}
|
||||||
|
|
||||||
type StateConvergencePlan {
|
type StateConvergencePlan {
|
||||||
uuid: String!
|
id: String!
|
||||||
running: Boolean!
|
running: Boolean!
|
||||||
actions: [ConvergenceAction]!
|
actions: [ConvergenceAction]!
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Manifest {
|
type Manifest {
|
||||||
uuid: String!
|
id: String!
|
||||||
created: Date!
|
created: Date!
|
||||||
type: ManifestType!
|
type: ManifestType!
|
||||||
format: ManifestFormat!
|
format: ManifestFormat!
|
||||||
@ -79,21 +79,21 @@
|
|||||||
|
|
||||||
# immutable
|
# immutable
|
||||||
type Service {
|
type Service {
|
||||||
uuid: String! # unique id for db row
|
id: String! # unique id for db row
|
||||||
hash: String! # unique id for version of service
|
hash: String! # unique id for version of service
|
||||||
name: String! # human readable name
|
name: String! # human readable name
|
||||||
slug: String!
|
slug: String!
|
||||||
instances: [Instance]!
|
instances: [Instance]!
|
||||||
# metrics: [MetricType]!
|
# metrics: [MetricType]!
|
||||||
currentMetrics: [CurrentMetric]!
|
currentMetrics: [CurrentMetric]!
|
||||||
connections: [String!] # list of serviceUuids
|
connections: [String!] # list of serviceIds
|
||||||
parent: ID # parent service uuid
|
parent: ID # parent service id
|
||||||
package: Package! # we don't have this in current mock data
|
package: Package! # we don't have this in current mock data
|
||||||
}
|
}
|
||||||
|
|
||||||
# for metrics max / min (I guess)
|
# for metrics max / min (I guess)
|
||||||
type Package {
|
type Package {
|
||||||
uuid: ID!
|
id: ID!
|
||||||
name: String!
|
name: String!
|
||||||
type: String!
|
type: String!
|
||||||
memory: Float!
|
memory: Float!
|
||||||
@ -115,7 +115,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Instance {
|
type Instance {
|
||||||
uuid: String!
|
id: String!
|
||||||
name: String!
|
name: String!
|
||||||
machineId: String!
|
machineId: String!
|
||||||
status: InstanceStatus!
|
status: InstanceStatus!
|
||||||
@ -123,8 +123,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Datacenter {
|
type Datacenter {
|
||||||
uuid: String!
|
id: String!
|
||||||
# name: String! # Do we have 'official' human readable names?
|
url: String!
|
||||||
|
name: String!
|
||||||
region: String!
|
region: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +141,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MetricType {
|
type MetricType {
|
||||||
uuid: String!
|
id: String!
|
||||||
name: String!
|
name: String!
|
||||||
id: String!
|
id: String!
|
||||||
}
|
}
|
||||||
@ -154,13 +155,13 @@
|
|||||||
type Query {
|
type Query {
|
||||||
portal: Portal
|
portal: Portal
|
||||||
deploymentGroups: [DeploymentGroup]
|
deploymentGroups: [DeploymentGroup]
|
||||||
deploymentGroup(uuid: String, slug: String): DeploymentGroup
|
deploymentGroup(id: String, slug: String): DeploymentGroup
|
||||||
services(deploymentGroupUuid: String, deploymentGroupSlug: String): [Service]
|
services(deploymentGroupId: String, deploymentGroupSlug: String): [Service]
|
||||||
service(uuid: String, slug: String): Service
|
service(id: String, slug: String): Service
|
||||||
instances(serviceUuid: String, serviceSlug: String): [Instance]
|
instances(serviceId: String, serviceSlug: String): [Instance]
|
||||||
instance(uuid: String, machineId: String): Instance
|
instance(id: String, machineId: String): Instance
|
||||||
metricTypes: [MetricType]
|
metricTypes: [MetricType]
|
||||||
metricData(instanceUuid: String!, metricType: String!, from: Date!, to: Date!): [InstanceMetric]!
|
metricData(instanceId: String!, metricType: String!, from: Date!, to: Date!): [InstanceMetric]!
|
||||||
package: Package
|
package: Package
|
||||||
datacenters: [Datacenter]
|
datacenters: [Datacenter]
|
||||||
# tmp test
|
# tmp test
|
||||||
@ -173,41 +174,42 @@
|
|||||||
portal: Portal
|
portal: Portal
|
||||||
user: User
|
user: User
|
||||||
deploymentGroups(name: String, slug: String): [DeploymentGroup]
|
deploymentGroups(name: String, slug: String): [DeploymentGroup]
|
||||||
deploymentGroup(uuid: ID, name: String, slug: String): DeploymentGroup
|
deploymentGroup(id: ID, name: String, slug: String): DeploymentGroup
|
||||||
serviceScales(serviceName: String, versionUuid: ID): [ServiceScale]
|
serviceScales(serviceName: String, versionId: ID): [ServiceScale]
|
||||||
serviceScale(uuid: ID!): ServiceScale
|
serviceScale(id: ID!): ServiceScale
|
||||||
convergenceActions(type: ConvergenceActionType, service: String, versionUuid: ID): [ConvergenceAction]
|
convergenceActions(type: ConvergenceActionType, service: String, versionId: ID): [ConvergenceAction]
|
||||||
convergenceAction(uuid: ID!): ConvergenceAction
|
convergenceAction(id: ID!): ConvergenceAction
|
||||||
stateConvergencePlans(running: Boolean, versionUuid: ID): [StateConvergencePlan]
|
stateConvergencePlans(running: Boolean, versionId: ID): [StateConvergencePlan]
|
||||||
stateConvergencePlan(uuid: ID!): StateConvergencePlan
|
stateConvergencePlan(id: ID!): StateConvergencePlan
|
||||||
versions(manifestUuid: ID, deploymentGroupUuid: ID): [Version]
|
versions(manifestId: ID, deploymentGroupId: ID): [Version]
|
||||||
version(uuid: ID, manifestUuid: ID): Version
|
version(id: ID, manifestId: ID): Version
|
||||||
manifests(type: String, deploymentGroupUuid: ID): [Manifest]
|
manifests(type: String, deploymentGroupId: ID): [Manifest]
|
||||||
manifest(uuid: ID!): Manifest
|
manifest(id: ID!): Manifest
|
||||||
services(name: String, slug: String, parentUuid: ID, deploymentGroupUuid: ID, deploymentGroupSlug: String): [Service]
|
services(name: String, slug: String, parentId: ID, deploymentGroupId: ID, deploymentGroupSlug: String): [Service]
|
||||||
service(uuid: ID, hash: ID): 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]
|
packages(name: String, type: String, memory: Int, disk: Int, swap: Int, lwps: Int, vcpus: Int, version: String, group: String): [Package]
|
||||||
package(uuid: ID!): Package
|
package(id: ID!): Package
|
||||||
instances(name: String!, machineId: ID, status: InstanceStatus, serviceUuid: ID, serviceSlug: String, deploymentGroupUuid: ID, deploymentGroupSlug: String): [Instance]
|
instances(name: String!, machineId: ID, status: InstanceStatus, serviceId: ID, serviceSlug: String, deploymentGroupId: ID, deploymentGroupSlug: String): [Instance]
|
||||||
instance(uuid: ID!): Instance
|
instance(id: ID!): Instance
|
||||||
datacenter(uuid: ID, region: String): Datacenter
|
datacenter(id: ID, region: String): Datacenter
|
||||||
|
datacenters: [Datacenter]
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
createDeploymentGroup(name: String!) : DeploymentGroup
|
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
|
scale(service: ID!, replicas: Int!) : Version
|
||||||
|
|
||||||
stopServices(uuids: [ID]!) : [Service]
|
stopServices(ids: [ID]!) : [Service]
|
||||||
startServices(uuids: [ID]!) : [Service]
|
startServices(ids: [ID]!) : [Service]
|
||||||
restartServices(uuids: [ID]!) : [Service]
|
restartServices(ids: [ID]!) : [Service]
|
||||||
deleteServices(uuids: [ID]!) : [Service]
|
deleteServices(ids: [ID]!) : [Service]
|
||||||
|
|
||||||
stopInstances(uuids: [ID]!) : [Instance]
|
stopInstances(ids: [ID]!) : [Instance]
|
||||||
startInstances(uuids: [ID]!) : [Instance]
|
startInstances(ids: [ID]!) : [Instance]
|
||||||
restartInstances(uuids: [ID]!) : [Instance]
|
restartInstances(ids: [ID]!) : [Instance]
|
||||||
|
|
||||||
# reprovision() ???
|
# 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';
|
'use strict';
|
||||||
|
|
||||||
|
const Schema = require('joyent-cp-gql-schema');
|
||||||
const Graphi = require('graphi');
|
const Graphi = require('graphi');
|
||||||
const PortalData = require('portal-data');
|
const PortalData = require('portal-data');
|
||||||
const Graphql = require('./models/graphql');
|
|
||||||
const Pack = require('../package.json');
|
const Pack = require('../package.json');
|
||||||
const Routes = require('./routes');
|
const Resolvers = require('./resolvers');
|
||||||
|
|
||||||
|
|
||||||
module.exports = function (server, options, next) {
|
module.exports = function (server, options, next) {
|
||||||
@ -19,12 +19,13 @@ module.exports = function (server, options, next) {
|
|||||||
server.register([
|
server.register([
|
||||||
{
|
{
|
||||||
register: Graphi,
|
register: Graphi,
|
||||||
options: Graphql.options(data)
|
options: {
|
||||||
|
schema: Schema,
|
||||||
|
resolvers: Resolvers(data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
server.route(Routes);
|
|
||||||
|
|
||||||
next();
|
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",
|
"graphi": "^2.0.0",
|
||||||
"hoek": "^4.1.1",
|
"hoek": "^4.1.1",
|
||||||
"joi": "^10.4.1",
|
"joi": "^10.4.1",
|
||||||
|
"joyent-cp-gql-schema": "^1.0.4",
|
||||||
"portal-data": "^1.0.0"
|
"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', () => {
|
describe('graphql', () => {
|
||||||
it('route exists', (done) => {
|
it('route exists', (done) => {
|
||||||
const server = new Hapi.Server();
|
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 Hoek = require('hoek');
|
||||||
const Penseur = require('penseur');
|
const Penseur = require('penseur');
|
||||||
const DCClient = require('docker-compose-client');
|
const DCClient = require('docker-compose-client');
|
||||||
const Awaitify = require('apr-awaitify');
|
const VAsync = require('vasync');
|
||||||
|
const Transform = require('./transform');
|
||||||
|
|
||||||
|
|
||||||
const internals = {
|
const internals = {
|
||||||
defaults: {
|
defaults: {
|
||||||
@ -13,13 +15,16 @@ const internals = {
|
|||||||
},
|
},
|
||||||
dockerHost: 'tcp://0.0.0.0:4242'
|
dockerHost: 'tcp://0.0.0.0:4242'
|
||||||
},
|
},
|
||||||
tables: [
|
tables: {
|
||||||
'activities',
|
'portals': { id: { type: 'uuid' } },
|
||||||
'datacenters',
|
'datacenters': { id: { type: 'uuid' } },
|
||||||
'deployments',
|
'deployment_groups': { id: { type: 'uuid' } },
|
||||||
'manifests',
|
'versions': { id: { type: 'uuid' } },
|
||||||
'metrics'
|
'manifests': { id: { type: 'uuid' } },
|
||||||
]
|
'services': { id: { type: 'uuid' } },
|
||||||
|
'packages': { id: { type: 'uuid' } },
|
||||||
|
'instances': { id: { type: 'uuid' } }
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = class Data {
|
module.exports = class Data {
|
||||||
@ -31,38 +36,83 @@ module.exports = class Data {
|
|||||||
this._docker = new DCClient(settings.dockerHost);
|
this._docker = new DCClient(settings.dockerHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
connect () {
|
connect (cb) {
|
||||||
return new Promise((resolve, reject) => {
|
this._db.establish(internals.tables, cb);
|
||||||
this._db.establish(internals.tables, (err) => {
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// portals
|
||||||
|
|
||||||
|
createPortal (clientPortal, cb) {
|
||||||
|
const portal = Transform.toPortal(clientPortal);
|
||||||
|
this._db.portals.insert(portal, (err, key) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return reject(err);
|
return cb(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// promisify Penseur
|
portal.id = key;
|
||||||
internals.tables.forEach((tableName) => {
|
cb(null, Transform.fromPortal({ portal }));
|
||||||
return ['insert', 'get', 'update', 'remove', 'all'].forEach((methodName) => {
|
|
||||||
this._db[tableName][methodName] = Awaitify(
|
|
||||||
this._db[tableName][methodName]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
resolve();
|
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 cb(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
cb(null, Transform.fromPortal({ portal, datacenter: results.successes[0], deploymentGroups: results.successes[1] }));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* DeploymentGroupUuid
|
// datacenters
|
||||||
* Manifest
|
|
||||||
* id: UUID,
|
createDatacenter (datacenter, cb) {
|
||||||
* created: Date.now(),
|
this._db.datacenters.insert(datacenter, (err, key) => {
|
||||||
* type: 'docker-compose',
|
if (err) {
|
||||||
* format: 'yml',
|
return cb(err);
|
||||||
* raw: 'original yml file content',
|
}
|
||||||
* obj: { }
|
|
||||||
*/
|
datacenter.id = key;
|
||||||
createDeployment ({ deploymentGroupUuid, manifest, deployment }) {
|
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
|
// trigger deployment
|
||||||
// create deployment queue (we should think about what is a deployment queue)
|
// create deployment queue (we should think about what is a deployment queue)
|
||||||
// create the ConvergencePlans
|
// create the ConvergencePlans
|
||||||
@ -70,111 +120,68 @@ module.exports = class Data {
|
|||||||
// create a Version
|
// create a Version
|
||||||
// update the DeploymentGroup
|
// update the DeploymentGroup
|
||||||
|
|
||||||
// TODO
|
this._db.deployment_groups.insert({ name }, (err, key) => {
|
||||||
const updateDb = (plan) => {
|
if (err) {
|
||||||
// deployment.services = [];
|
return cb(err);
|
||||||
// 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) {
|
cb(null, Transform.fromDeploymentGroup({ id: key, name }));
|
||||||
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)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const entry = { id: containerId, metrics };
|
updateDeploymentGroup ({ id, name }, cb) {
|
||||||
return this._db.metrics.insert(entry, { merge: true });
|
this._db.deployment_groups.update(id, { name }, (err) => {
|
||||||
|
if (err) {
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
cb(null, Transform.fromDeploymentGroup({ id, name }));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getServices (deploymentId) {
|
getDeploymentGroups (ids, cb) {
|
||||||
this._db.deployments.get(deploymentId, { filter: 'services' });
|
this._db.deployment_groups.get(ids, (err, deploymentGroups) => {
|
||||||
|
if (err) {
|
||||||
|
return cb(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateService (deploymentId, service) {
|
deploymentGroups = deploymentGroups || [];
|
||||||
this._db.deployments.get(deploymentId, { filter: 'services' }).then((deployment) => {
|
cb(null, deploymentGroups.map(Transform.fromDeploymentGroup));
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._db.deployments.update(deploymentId, {
|
|
||||||
services: deployment.services
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deploymentChanges (handler) {
|
getDeploymentGroup (id, cb) {
|
||||||
return this._db.deployments.changes('*', { reconnect: true, handler });
|
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": {
|
"scripts": {
|
||||||
"bootstrap": "node ./bootstrap-data",
|
"bootstrap": "node ./bootstrap-data",
|
||||||
"lint": "belly-button",
|
"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-up": "docker run -d -p 8080:8080 -p 28015:28015 -p 29015:29015 --name rethinkdb rethinkdb",
|
||||||
"rethinkdb-down": "docker rm -f rethinkdb",
|
"rethinkdb-down": "docker rm -f rethinkdb",
|
||||||
"test": "exit 0 # npm run lint && lab -t 40"
|
"test": "exit 0 # npm run lint && lab -t 40"
|
||||||
@ -15,10 +14,10 @@
|
|||||||
"author": "wyatt",
|
"author": "wyatt",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"apr-awaitify": "^1.0.4",
|
|
||||||
"docker-compose-client": "^1.0.3",
|
"docker-compose-client": "^1.0.3",
|
||||||
"hoek": "^4.1.1",
|
"hoek": "^4.1.1",
|
||||||
"penseur": "^7.8.1"
|
"penseur": "^7.8.1",
|
||||||
|
"vasync": "^1.6.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"belly-button": "^3.1.0",
|
"belly-button": "^3.1.0",
|
||||||
|
@ -12,204 +12,59 @@ const internals = {
|
|||||||
options: { name: 'test', db: { test: true } }
|
options: { name: 'test', db: { test: true } }
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('connect()', function () {
|
describe('connect()', () => {
|
||||||
it('connects to the database', () => {
|
it('connects to the database', (done) => {
|
||||||
const data = new PortalData(internals.options);
|
const data = new PortalData(internals.options);
|
||||||
|
data.connect(done);
|
||||||
return data.connect();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe.skip('createDeployment()', () => {
|
describe('portals', () => {
|
||||||
it('creates a deployment record in the deployment table', (done) => {
|
describe('createPortal()', () => {
|
||||||
|
it('creates a new portal', (done) => {
|
||||||
const data = new PortalData(internals.options);
|
const data = new PortalData(internals.options);
|
||||||
const deployment = {
|
data.connect((err) => {
|
||||||
name: 'User Services',
|
|
||||||
datacenter: 'us-sw-1'
|
|
||||||
};
|
|
||||||
|
|
||||||
data
|
|
||||||
.connect()
|
|
||||||
.then(() => {
|
|
||||||
data
|
|
||||||
.createDeployment({
|
|
||||||
deployment
|
|
||||||
})
|
|
||||||
.then((deployment) => {
|
|
||||||
expect(deployment.id).to.exist();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
expect(err).to.not.exist();
|
expect(err).to.not.exist();
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe.skip('getDeployment()', () => {
|
const portal = {
|
||||||
it('will retrieve an existing deployment', (done) => {
|
username: 'tom'
|
||||||
const data = new PortalData(internals.options);
|
|
||||||
data.connect().then(() => {
|
|
||||||
const deployment = {
|
|
||||||
name: 'User Services',
|
|
||||||
datacenter: 'us-sw-1'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
data.createDeployment(deployment).then((deployment) => {
|
data.createPortal(portal, (err, result) => {
|
||||||
expect(deployment.id).to.exist();
|
|
||||||
data.getDeployment(deployment.id).then((retrievedDeployment) => {
|
|
||||||
expect(deployment).to.equal(retrievedDeployment);
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
data.createDeployment(deployment).then((deployment) => {
|
|
||||||
expect(deployment.id).to.exist();
|
|
||||||
data.updateService(deployment.id, service).then((updatedService) => {
|
|
||||||
expect(updatedService).to.equal(service);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
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) => {
|
|
||||||
expect(err).to.not.exist();
|
expect(err).to.not.exist();
|
||||||
if (executed) {
|
expect(result.id).to.exist();
|
||||||
return;
|
expect(result.username).to.equal(portal.username);
|
||||||
}
|
|
||||||
|
|
||||||
expect(changes.before).to.exist();
|
|
||||||
expect(changes.after).to.exist();
|
|
||||||
done();
|
done();
|
||||||
executed = true;
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
data
|
|
||||||
.updateService(deployment.id, service2)
|
|
||||||
.then((updatedService2) => {
|
|
||||||
expect(updatedService2).to.equal(service2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe.skip('insertMetrics()', () => {
|
describe('getPortal()', () => {
|
||||||
it("will add new metrics to a service and won't overwrite existing ones", (done) => {
|
it('retrieves a single portal record', (done) => {
|
||||||
const data = new PortalData(internals.options);
|
const data = new PortalData(internals.options);
|
||||||
data.connect().then(() => {
|
data.connect((err) => {
|
||||||
const containerId = '81205d4a-92f4-c4d9-da8a-aafd689eeabb';
|
expect(err).to.not.exist();
|
||||||
const metrics1 = [
|
const datacenter = {
|
||||||
{
|
region: 'us-sw-1'
|
||||||
timestamp: 1494360995851,
|
};
|
||||||
cpu: 1.2,
|
|
||||||
memory: 23344523,
|
|
||||||
network: 5024
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const metrics2 = [
|
data.createDatacenter(datacenter, (err, createdDatacenter) => {
|
||||||
{
|
expect(err).to.not.exist();
|
||||||
timestamp: 1495360995851,
|
const portal = {
|
||||||
cpu: 1.3,
|
username: 'tom',
|
||||||
memory: 23344523,
|
datacenter: {
|
||||||
network: 4024
|
id: createdDatacenter.id
|
||||||
}
|
}
|
||||||
];
|
};
|
||||||
|
|
||||||
data.insertMetrics(containerId, metrics1).then((result1) => {
|
data.createPortal(portal, (err, createdPortal) => {
|
||||||
expect(result1.id).to.equal(containerId);
|
expect(err).to.not.exist();
|
||||||
expect(result1.metrics).to.equal(metrics1);
|
expect(createdPortal.id).to.exist();
|
||||||
data.insertMetrics(containerId, metrics2).then((result2) => {
|
data.getPortal((err, retrievedPortal) => {
|
||||||
expect(result2.id).to.equal(containerId);
|
expect(err).to.not.exist();
|
||||||
data.getMetrics(containerId).then((results) => {
|
expect(retrievedPortal.id).to.exist();
|
||||||
expect(results.metrics.length).to.equal(2);
|
expect(retrievedPortal.username).to.equal(portal.username);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -217,3 +72,192 @@ describe.skip('insertMetrics()', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
|
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:
|
doctrine@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63"
|
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"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
|
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:
|
fast-levenshtein@~2.0.4:
|
||||||
version "2.0.6"
|
version "2.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
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"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"
|
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:
|
window-size@0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
|
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
|
||||||
|
Loading…
Reference in New Issue
Block a user