add portal-api
This commit is contained in:
parent
4e73cc4ea6
commit
d5ecdd1cbc
3
portal-api/.gitignore
vendored
Normal file
3
portal-api/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
npm-debug.log
|
8
portal-api/.travis.yml
Normal file
8
portal-api/.travis.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
language: node_js
|
||||||
|
|
||||||
|
node_js:
|
||||||
|
- "6"
|
||||||
|
- "7"
|
||||||
|
- "node"
|
||||||
|
|
||||||
|
sudo: false
|
362
portal-api/LICENSE
Normal file
362
portal-api/LICENSE
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
Mozilla Public License, version 2.0
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
|
||||||
|
means each individual or legal entity that creates, contributes to the
|
||||||
|
creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
|
||||||
|
means the combination of the Contributions of others (if any) used by a
|
||||||
|
Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
|
||||||
|
means Source Code Form to which the initial Contributor has attached the
|
||||||
|
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||||
|
Modifications of such Source Code Form, in each case including portions
|
||||||
|
thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
a. that the initial Contributor has attached the notice described in
|
||||||
|
Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
b. that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the terms of
|
||||||
|
a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
|
||||||
|
means a work that combines Covered Software with other material, in a
|
||||||
|
separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
|
||||||
|
means having the right to grant, to the maximum extent possible, whether
|
||||||
|
at the time of the initial grant or subsequently, any and all of the
|
||||||
|
rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
a. any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered Software; or
|
||||||
|
|
||||||
|
b. any new file in Source Code Form that contains any Covered Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the License,
|
||||||
|
by the making, using, selling, offering for sale, having made, import,
|
||||||
|
or transfer of either its Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||||
|
General Public License, Version 2.1, the GNU Affero General Public
|
||||||
|
License, Version 3.0, or any later versions of those licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that controls, is
|
||||||
|
controlled by, or is under common control with You. For purposes of this
|
||||||
|
definition, "control" means (a) the power, direct or indirect, to cause
|
||||||
|
the direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||||
|
outstanding shares or beneficial ownership of such entity.
|
||||||
|
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
a. under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||||
|
sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
a. for any code that a Contributor has removed from Covered Software; or
|
||||||
|
|
||||||
|
b. for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
c. under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights to
|
||||||
|
grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||||
|
Section 2.1.
|
||||||
|
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
a. such Covered Software must also be made available in Source Code Form,
|
||||||
|
as described in Section 3.1, and You must inform recipients of the
|
||||||
|
Executable Form how they can obtain a copy of such Source Code Form by
|
||||||
|
reasonable means in a timely manner, at a charge no more than the cost
|
||||||
|
of distribution to the recipient; and
|
||||||
|
|
||||||
|
b. You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter the
|
||||||
|
recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||||
|
limitations of liability) contained within the Source Code Form of the
|
||||||
|
Covered Software, except that You may alter any license notices to the
|
||||||
|
extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this License
|
||||||
|
with respect to some or all of the Covered Software due to statute,
|
||||||
|
judicial order, or regulation then You must: (a) comply with the terms of
|
||||||
|
this License to the maximum extent possible; and (b) describe the
|
||||||
|
limitations and the code they affect. Such description must be placed in a
|
||||||
|
text file included with all distributions of the Covered Software under
|
||||||
|
this License. Except to the extent prohibited by statute or regulation,
|
||||||
|
such description must be sufficiently detailed for a recipient of ordinary
|
||||||
|
skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically if You
|
||||||
|
fail to comply with any of its terms. However, if You become compliant,
|
||||||
|
then the rights granted under this License from a particular Contributor
|
||||||
|
are reinstated (a) provisionally, unless and until such Contributor
|
||||||
|
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||||
|
basis, if such Contributor fails to notify You of the non-compliance by
|
||||||
|
some reasonable means prior to 60 days after You have come back into
|
||||||
|
compliance. Moreover, Your grants from a particular Contributor are
|
||||||
|
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||||
|
non-compliance by some reasonable means, this is the first time You have
|
||||||
|
received notice of non-compliance with this License from such
|
||||||
|
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||||
|
of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||||
|
license agreements (excluding distributors and resellers) which have been
|
||||||
|
validly granted by You or Your distributors under this License prior to
|
||||||
|
termination shall survive termination.
|
||||||
|
|
||||||
|
6. Disclaimer of Warranty
|
||||||
|
|
||||||
|
Covered Software is provided under this License on an "as is" basis,
|
||||||
|
without warranty of any kind, either expressed, implied, or statutory,
|
||||||
|
including, without limitation, warranties that the Covered Software is free
|
||||||
|
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||||
|
The entire risk as to the quality and performance of the Covered Software
|
||||||
|
is with You. Should any Covered Software prove defective in any respect,
|
||||||
|
You (not any Contributor) assume the cost of any necessary servicing,
|
||||||
|
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||||
|
part of this License. No use of any Covered Software is authorized under
|
||||||
|
this License except under this disclaimer.
|
||||||
|
|
||||||
|
7. Limitation of Liability
|
||||||
|
|
||||||
|
Under no circumstances and under no legal theory, whether tort (including
|
||||||
|
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||||
|
distributes Covered Software as permitted above, be liable to You for any
|
||||||
|
direct, indirect, special, incidental, or consequential damages of any
|
||||||
|
character including, without limitation, damages for lost profits, loss of
|
||||||
|
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses, even if such party shall have been
|
||||||
|
informed of the possibility of such damages. This limitation of liability
|
||||||
|
shall not apply to liability for death or personal injury resulting from
|
||||||
|
such party's negligence to the extent applicable law prohibits such
|
||||||
|
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||||
|
incidental or consequential damages, so this exclusion and limitation may
|
||||||
|
not apply to You.
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the courts
|
||||||
|
of a jurisdiction where the defendant maintains its principal place of
|
||||||
|
business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||||
|
in this Section shall prevent a party's ability to bring cross-claims or
|
||||||
|
counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides that
|
||||||
|
the language of a contract shall be construed against the drafter shall not
|
||||||
|
be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses If You choose to distribute Source Code Form that is
|
||||||
|
Incompatible With Secondary Licenses under the terms of this version of
|
||||||
|
the License, the notice described in Exhibit B of this License must be
|
||||||
|
attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
|
||||||
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular file,
|
||||||
|
then You may include the notice in a location (such as a LICENSE file in a
|
||||||
|
relevant directory) where a recipient would be likely to look for such a
|
||||||
|
notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible
|
||||||
|
With Secondary Licenses", as defined by
|
||||||
|
the Mozilla Public License, v. 2.0.
|
14
portal-api/README.md
Normal file
14
portal-api/README.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
ContainerPilot Monitor API
|
||||||
|
|
||||||
|
[![Build Status](https://secure.travis-ci.org/geek/portal-api.svg)](http://travis-ci.org/geek/portal-api)
|
||||||
|
|
||||||
|
Lead Maintainer: [Wyatt Preul](https://github.com/geek)
|
||||||
|
|
||||||
|
|
||||||
|
## Example usage
|
||||||
|
|
||||||
|
```
|
||||||
|
NODE_ENV=dev node example
|
||||||
|
```
|
||||||
|
|
||||||
|
Navigate to http://localhost:8000/graphiql to run graphQL queries. Navigate to http://localhost:8000/documentation for documentation on each route.
|
42
portal-api/example.js
Normal file
42
portal-api/example.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Hapi = require('hapi');
|
||||||
|
const Inert = require('inert');
|
||||||
|
const Vision = require('vision');
|
||||||
|
const HapiSwagger = require('hapi-swagger');
|
||||||
|
const Pack = require('./package');
|
||||||
|
const Portal = require('./lib');
|
||||||
|
|
||||||
|
const server = new Hapi.Server();
|
||||||
|
server.connection({ port: 8000 });
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
info: {
|
||||||
|
'title': 'Portal API Documentation',
|
||||||
|
'version': Pack.version
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
server.register([
|
||||||
|
Inert,
|
||||||
|
Vision,
|
||||||
|
Portal,
|
||||||
|
{
|
||||||
|
register: HapiSwagger,
|
||||||
|
options
|
||||||
|
}],
|
||||||
|
(err) => {
|
||||||
|
handlerError(err);
|
||||||
|
server.start((err) => {
|
||||||
|
handlerError(err);
|
||||||
|
console.log(`server started at http://localhost:${server.info.port}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function handlerError (error) {
|
||||||
|
if (error) {
|
||||||
|
console.error(error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
86
portal-api/lib/data.js
Normal file
86
portal-api/lib/data.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Examples = require('./models/examples');
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = class Data {
|
||||||
|
constructor (options) {
|
||||||
|
this._options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
createDeployment (deployment) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(Examples.deployment);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getDeployment (id) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(Examples.deployment);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
updateDeployment (deployment) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(Examples.deployment);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
deleteDeployment (id) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getDeployments () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(Examples.deployments);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getDatacenters () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(Examples.datacenters);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createManifest (deploymentId, manifest) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(Examples.manifest);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getManifest (deploymentId, revision) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(Examples.manifest);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getActivities (deploymentId) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(Examples.activities);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getMetrics (deploymentId) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(Examples.metrics);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getState (deploymentId) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(Examples.state);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
updateState (deploymentId, action) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(Examples.state);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getServices (deploymentId) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(Examples.services);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
updateService (deploymentId, service) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(Examples.service);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
93
portal-api/lib/handlers.js
Normal file
93
portal-api/lib/handlers.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
'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.deploymentId));
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.deploymentUpdate = function (request, reply) {
|
||||||
|
const payload = request.payload;
|
||||||
|
payload.id = request.deploymentId;
|
||||||
|
|
||||||
|
reply(this.updateDeployment(payload));
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.deploymentDelete = function (request, reply) {
|
||||||
|
reply(this.deleteDeployment(request.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('{revision}', manifest.revision));
|
||||||
|
}).catch((error) => {
|
||||||
|
reply(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.manifestGet = function (request, reply) {
|
||||||
|
reply(this.getManifest(request.params.deploymentId, request.params.revision));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Deployment Group State
|
||||||
|
|
||||||
|
exports.stateGet = function (request, reply) {
|
||||||
|
reply(this.getState(request.params.deploymentId));
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.stateUpdate = function (request, reply) {
|
||||||
|
reply(this.updateState(request.params.deploymentId, request.payload.action));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
};
|
51
portal-api/lib/index.js
Normal file
51
portal-api/lib/index.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const GraphqlHapi = require('graphql-server-hapi');
|
||||||
|
const Data = require('./data');
|
||||||
|
const Graphql = require('./models/graphql');
|
||||||
|
const Pack = require('../package.json');
|
||||||
|
const Routes = require('./routes');
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = function (server, options, next) {
|
||||||
|
const data = new Data(options.data);
|
||||||
|
server.bind(data);
|
||||||
|
|
||||||
|
|
||||||
|
server.register([
|
||||||
|
{
|
||||||
|
register: GraphqlHapi.graphqlHapi,
|
||||||
|
options: {
|
||||||
|
path: '/graphql',
|
||||||
|
graphqlOptions: Graphql.options(data),
|
||||||
|
route: {
|
||||||
|
cors: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'dev') {
|
||||||
|
server.register({
|
||||||
|
register: GraphqlHapi.graphiqlHapi,
|
||||||
|
options: {
|
||||||
|
path: '/graphiql',
|
||||||
|
graphiqlOptions: Graphql.options(data),
|
||||||
|
route: {
|
||||||
|
cors: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
server.route(Routes);
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.attributes = {
|
||||||
|
name: Pack.name,
|
||||||
|
version: Pack.version,
|
||||||
|
once: true,
|
||||||
|
multiple: false
|
||||||
|
};
|
96
portal-api/lib/models/examples.js
Normal file
96
portal-api/lib/models/examples.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
'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.deployments = [{
|
||||||
|
id: 42,
|
||||||
|
name: 'User Services',
|
||||||
|
datacenter: 'us-sw-1'
|
||||||
|
}];
|
||||||
|
|
||||||
|
exports.deployment = exports.deployments[0];
|
||||||
|
|
||||||
|
|
||||||
|
exports.manifest = {
|
||||||
|
revision: 5,
|
||||||
|
file: {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
exports.services = [
|
||||||
|
{
|
||||||
|
name: 'consul',
|
||||||
|
count: 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'prometheus',
|
||||||
|
count: 1
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
exports.service = exports.services[0];
|
||||||
|
|
||||||
|
|
||||||
|
exports.state = {
|
||||||
|
current: 'started'
|
||||||
|
};
|
155
portal-api/lib/models/graphql.js
Normal file
155
portal-api/lib/models/graphql.js
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Graphql = require('graphql');
|
||||||
|
|
||||||
|
|
||||||
|
const internals = {
|
||||||
|
schema: `
|
||||||
|
scalar Object
|
||||||
|
scalar Date
|
||||||
|
|
||||||
|
type Activity {
|
||||||
|
date: Date!
|
||||||
|
type: String!
|
||||||
|
meta: Object
|
||||||
|
}
|
||||||
|
|
||||||
|
type Datacenter {
|
||||||
|
name: String!
|
||||||
|
url: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
input DeploymentCreate {
|
||||||
|
name: String!
|
||||||
|
datacenter: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
input DeploymentUpdate {
|
||||||
|
id: ID!
|
||||||
|
name: String!
|
||||||
|
datacenter: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Deployment {
|
||||||
|
id: ID!
|
||||||
|
name: String!
|
||||||
|
datacenter: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Manifest {
|
||||||
|
revision: Int!
|
||||||
|
file: Object!
|
||||||
|
}
|
||||||
|
|
||||||
|
input ManifestCreate {
|
||||||
|
file: Object!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Metric {
|
||||||
|
service: String!
|
||||||
|
cpu: Float!
|
||||||
|
memory: Float!
|
||||||
|
network: Float!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service {
|
||||||
|
name: String!
|
||||||
|
count: Int!
|
||||||
|
}
|
||||||
|
|
||||||
|
input ServiceUpdate {
|
||||||
|
name: String!
|
||||||
|
count: Int!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Mutation {
|
||||||
|
createDeployment(deployment: DeploymentCreate!): Deployment!
|
||||||
|
deleteDeployment(deploymentId: ID!): String
|
||||||
|
updateDeployment(deployment: DeploymentUpdate!): Deployment!
|
||||||
|
createManifest(deploymentId: ID!, manifest: ManifestCreate!): Manifest!
|
||||||
|
updateService(deploymentId: ID!, service: ServiceUpdate!): Service!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Query {
|
||||||
|
getActivities: [Activity]
|
||||||
|
getDatacenters: [Datacenter]
|
||||||
|
getDeployment(id: ID!): Deployment
|
||||||
|
getDeployments: [Deployment]
|
||||||
|
getManifest(deploymentId: ID!, revision: Int!): Manifest
|
||||||
|
getMetrics(deploymentId: ID!): [Metric]
|
||||||
|
getServices(deploymentId: ID!): [Service]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
exports.options = (data) => {
|
||||||
|
const schema = Graphql.buildSchema(internals.schema);
|
||||||
|
|
||||||
|
const createDeployment = function (args) {
|
||||||
|
return data.createDeployment(args.deployment);
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteDeployment = function (args) {
|
||||||
|
return data.deleteDeployment(args.deploymentId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateDeployment = function (args) {
|
||||||
|
return data.updateDeployment(args.deployment);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createManifest = function (args) {
|
||||||
|
return data.createManifest(args.deploymentId, args.manifest);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateService = function (args) {
|
||||||
|
return data.updateService(args.deploymentId, args.service);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getActivities = function () {
|
||||||
|
return data.getActivities();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDatacenters = function () {
|
||||||
|
return data.getDatacenters();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDeployment = function (args) {
|
||||||
|
return data.getDeployment(args.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDeployments = function () {
|
||||||
|
return data.getDeployments();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getManifest = function (args) {
|
||||||
|
return data.getManifest(args.deploymentId, args.revision);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getMetrics = function (args) {
|
||||||
|
return data.getMetrics(args.deploymentId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getServices = function (args) {
|
||||||
|
return data.v(args.deploymentId);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
schema,
|
||||||
|
endpointURL: '/graphql',
|
||||||
|
rootValue: {
|
||||||
|
createDeployment,
|
||||||
|
deleteDeployment,
|
||||||
|
updateDeployment,
|
||||||
|
createManifest,
|
||||||
|
updateService,
|
||||||
|
getActivities,
|
||||||
|
getDatacenters,
|
||||||
|
getDeployment,
|
||||||
|
getDeployments,
|
||||||
|
getManifest,
|
||||||
|
getMetrics,
|
||||||
|
getServices
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
109
portal-api/lib/models/index.js
Normal file
109
portal-api/lib/models/index.js
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
'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);
|
||||||
|
|
||||||
|
|
||||||
|
// Deployments
|
||||||
|
|
||||||
|
exports.deploymentId = Joi.number().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
|
||||||
|
}).example(Examples.deployments[0]);
|
||||||
|
|
||||||
|
exports.deployments = Joi.array().items(exports.deployment);
|
||||||
|
|
||||||
|
|
||||||
|
// Manifests
|
||||||
|
|
||||||
|
exports.manifestRevision = Joi.number().required().description('Revision number of manifest').example(Examples.manifest.revision);
|
||||||
|
|
||||||
|
exports.manifestCreate = Joi.object({
|
||||||
|
file: Joi.object().required().description('Manifest file represented as JSON').example(Examples.manifest.file)
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.manifest = exports.manifestCreate.keys({
|
||||||
|
revision: exports.manifestRevision
|
||||||
|
}).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);
|
||||||
|
|
||||||
|
|
||||||
|
// 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'])
|
||||||
|
.description('The current state of the deployment group')
|
||||||
|
});
|
259
portal-api/lib/routes.js
Normal file
259
portal-api/lib/routes.js
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
'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/{revision}',
|
||||||
|
method: 'get',
|
||||||
|
config: {
|
||||||
|
id: 'manifestGet',
|
||||||
|
tags: ['api', 'deployment', 'manifest'],
|
||||||
|
description: 'Retrieve a manifest revision for a deployment group',
|
||||||
|
validate: {
|
||||||
|
params: {
|
||||||
|
deploymentId: Models.deploymentId,
|
||||||
|
revision: Models.manifestRevision
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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}/state',
|
||||||
|
method: 'get',
|
||||||
|
config: {
|
||||||
|
tags: ['api', 'deployment', 'state'],
|
||||||
|
description: 'Retrieve the current state of the deployment group',
|
||||||
|
validate: {
|
||||||
|
params: {
|
||||||
|
deploymentId: Models.deploymentId
|
||||||
|
}
|
||||||
|
},
|
||||||
|
response: {
|
||||||
|
schema: Models.state
|
||||||
|
},
|
||||||
|
handler: Handlers.stateGet
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/deployment/{deploymentId}/state',
|
||||||
|
method: 'put',
|
||||||
|
config: {
|
||||||
|
tags: ['api', 'deployment', 'state'],
|
||||||
|
description: 'Perform an action on the deployment group state',
|
||||||
|
validate: {
|
||||||
|
params: {
|
||||||
|
deploymentId: Models.deploymentId
|
||||||
|
},
|
||||||
|
payload: Models.stateAction
|
||||||
|
},
|
||||||
|
response: {
|
||||||
|
schema: Models.state
|
||||||
|
},
|
||||||
|
handler: Handlers.stateUpdate
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
28
portal-api/package.json
Normal file
28
portal-api/package.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "portal-api",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "./lib/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"lint": "belly-button",
|
||||||
|
"test": "npm run lint && lab -t 97"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "wyatt",
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"belly-button": "^3.1.0",
|
||||||
|
"code": "^4.0.0",
|
||||||
|
"hapi": "^16.1.1",
|
||||||
|
"hapi-swagger": "^7.7.0",
|
||||||
|
"inert": "^4.2.0",
|
||||||
|
"lab": "^13.0.2",
|
||||||
|
"vision": "^4.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"boom": "^4.3.1",
|
||||||
|
"graphql": "^0.9.3",
|
||||||
|
"graphql-server-hapi": "^0.7.2",
|
||||||
|
"joi": "^10.4.1"
|
||||||
|
}
|
||||||
|
}
|
276
portal-api/test/index.js
Normal file
276
portal-api/test/index.js
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Code = require('code');
|
||||||
|
const Hapi = require('hapi');
|
||||||
|
const Lab = require('lab');
|
||||||
|
const PortalApi = require('../');
|
||||||
|
|
||||||
|
|
||||||
|
// Test shortcuts
|
||||||
|
|
||||||
|
const lab = exports.lab = Lab.script();
|
||||||
|
const describe = lab.describe;
|
||||||
|
const it = lab.it;
|
||||||
|
const expect = Code.expect;
|
||||||
|
|
||||||
|
|
||||||
|
describe('portal-api plugin', () => {
|
||||||
|
it('can be registered with hapi', (done) => {
|
||||||
|
const server = new Hapi.Server();
|
||||||
|
server.connection();
|
||||||
|
server.register(PortalApi, (err) => {
|
||||||
|
expect(err).to.not.exist();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('deployments', () => {
|
||||||
|
it('can be created', (done) => {
|
||||||
|
const server = new Hapi.Server();
|
||||||
|
server.connection();
|
||||||
|
server.register(PortalApi, (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(PortalApi, (err) => {
|
||||||
|
expect(err).to.not.exist();
|
||||||
|
const payload = {
|
||||||
|
name: 'User Services',
|
||||||
|
datacenter: 'us-sw-1'
|
||||||
|
};
|
||||||
|
|
||||||
|
server.inject({ method: 'PUT', url: '/deployment/42', payload }, (res) => {
|
||||||
|
expect(res.statusCode).to.equal(200);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be retrieved', (done) => {
|
||||||
|
const server = new Hapi.Server();
|
||||||
|
server.connection();
|
||||||
|
server.register(PortalApi, (err) => {
|
||||||
|
expect(err).to.not.exist();
|
||||||
|
|
||||||
|
server.inject({ method: 'GET', url: '/deployment/42' }, (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(PortalApi, (err) => {
|
||||||
|
expect(err).to.not.exist();
|
||||||
|
|
||||||
|
server.inject({ method: 'DELETE', url: '/deployment/42' }, (res) => {
|
||||||
|
expect(res.statusCode).to.equal(200);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can all be retrieved', (done) => {
|
||||||
|
const server = new Hapi.Server();
|
||||||
|
server.connection();
|
||||||
|
server.register(PortalApi, (err) => {
|
||||||
|
expect(err).to.not.exist();
|
||||||
|
|
||||||
|
server.inject({ method: 'GET', url: '/deployments' }, (res) => {
|
||||||
|
expect(res.statusCode).to.equal(200);
|
||||||
|
expect(res.result.length).to.equal(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('datacenters', () => {
|
||||||
|
it('can be retrieved', (done) => {
|
||||||
|
const server = new Hapi.Server();
|
||||||
|
server.connection();
|
||||||
|
server.register(PortalApi, (err) => {
|
||||||
|
expect(err).to.not.exist();
|
||||||
|
|
||||||
|
server.inject({ method: 'GET', url: '/datacenters' }, (res) => {
|
||||||
|
expect(res.statusCode).to.equal(200);
|
||||||
|
expect(res.result.length).to.equal(2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('manifests', () => {
|
||||||
|
it('can be created', (done) => {
|
||||||
|
const server = new Hapi.Server();
|
||||||
|
server.connection();
|
||||||
|
server.register(PortalApi, (err) => {
|
||||||
|
expect(err).to.not.exist();
|
||||||
|
const payload = {
|
||||||
|
file: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
server.inject({ method: 'POST', url: '/deployment/42/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(PortalApi, (err) => {
|
||||||
|
expect(err).to.not.exist();
|
||||||
|
|
||||||
|
server.inject({ method: 'GET', url: '/deployment/42/manifest/5' }, (res) => {
|
||||||
|
expect(res.statusCode).to.equal(200);
|
||||||
|
expect(res.result.file).to.exist();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('activities', () => {
|
||||||
|
it('can be retrieved', (done) => {
|
||||||
|
const server = new Hapi.Server();
|
||||||
|
server.connection();
|
||||||
|
server.register(PortalApi, (err) => {
|
||||||
|
expect(err).to.not.exist();
|
||||||
|
|
||||||
|
server.inject({ method: 'GET', url: '/deployment/42/activities' }, (res) => {
|
||||||
|
expect(res.statusCode).to.equal(200);
|
||||||
|
expect(res.result.length).to.equal(2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('metrics', () => {
|
||||||
|
it('can be retrieved', (done) => {
|
||||||
|
const server = new Hapi.Server();
|
||||||
|
server.connection();
|
||||||
|
server.register(PortalApi, (err) => {
|
||||||
|
expect(err).to.not.exist();
|
||||||
|
|
||||||
|
server.inject({ method: 'GET', url: '/deployment/42/metrics' }, (res) => {
|
||||||
|
expect(res.statusCode).to.equal(200);
|
||||||
|
expect(res.result.length).to.equal(2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('deployment state', () => {
|
||||||
|
it('can be retrieved', (done) => {
|
||||||
|
const server = new Hapi.Server();
|
||||||
|
server.connection();
|
||||||
|
server.register(PortalApi, (err) => {
|
||||||
|
expect(err).to.not.exist();
|
||||||
|
|
||||||
|
server.inject({ method: 'GET', url: '/deployment/42/state' }, (res) => {
|
||||||
|
expect(res.statusCode).to.equal(200);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be updated', (done) => {
|
||||||
|
const server = new Hapi.Server();
|
||||||
|
server.connection();
|
||||||
|
server.register(PortalApi, (err) => {
|
||||||
|
expect(err).to.not.exist();
|
||||||
|
const payload = {
|
||||||
|
action: 'restart'
|
||||||
|
};
|
||||||
|
|
||||||
|
server.inject({ method: 'PUT', url: '/deployment/42/state', payload }, (res) => {
|
||||||
|
expect(res.statusCode).to.equal(200);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('services', () => {
|
||||||
|
it('can be retrieved', (done) => {
|
||||||
|
const server = new Hapi.Server();
|
||||||
|
server.connection();
|
||||||
|
server.register(PortalApi, (err) => {
|
||||||
|
expect(err).to.not.exist();
|
||||||
|
|
||||||
|
server.inject({ method: 'GET', url: '/deployment/42/services' }, (res) => {
|
||||||
|
expect(res.statusCode).to.equal(200);
|
||||||
|
expect(res.result.length).to.equal(2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be updated', (done) => {
|
||||||
|
const server = new Hapi.Server();
|
||||||
|
server.connection();
|
||||||
|
server.register(PortalApi, (err) => {
|
||||||
|
expect(err).to.not.exist();
|
||||||
|
const payload = {
|
||||||
|
count: 3
|
||||||
|
};
|
||||||
|
|
||||||
|
server.inject({ method: 'PUT', url: '/deployment/42/service/consul', payload }, (res) => {
|
||||||
|
expect(res.statusCode).to.equal(200);
|
||||||
|
expect(res.result.count).to.equal(3);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('graphql', () => {
|
||||||
|
it('route exists', (done) => {
|
||||||
|
const server = new Hapi.Server();
|
||||||
|
server.connection();
|
||||||
|
server.register(PortalApi, (err) => {
|
||||||
|
expect(err).to.not.exist();
|
||||||
|
const url = '/graphql?query=%7B%0A%20%20getDeployment(id%3A%201)%20%7B%0A%20%20%20%20id%0A%20%20%7D%0A%7D';
|
||||||
|
|
||||||
|
server.inject({ method: 'GET', url }, (res) => {
|
||||||
|
expect(res.statusCode).to.equal(200);
|
||||||
|
const result = JSON.parse(res.result);
|
||||||
|
expect(result.data).to.exist();
|
||||||
|
expect(result.data.getDeployment).to.exist();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user