Compare commits
3 Commits
developmen
...
epic/ZENKO
Author | SHA1 | Date |
---|---|---|
bbuchanan9 | 8176560b2e | |
bbuchanan9 | d6752875e6 | |
bbuchanan9 | 0cf63d6fe3 |
|
@ -0,0 +1 @@
|
||||||
|
node_modules
|
17
Dockerfile
17
Dockerfile
|
@ -1,19 +1,16 @@
|
||||||
FROM node:6-slim
|
FROM node:8-slim
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
COPY package.json /usr/src/app
|
COPY . /usr/src/app
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y jq --no-install-recommends \
|
&& apt-get install git -y \
|
||||||
&& npm install --production \
|
&& apt install build-essential -y \
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& apt-get install python -y \
|
||||||
|
&& apt-get install g++ -y \
|
||||||
&& npm cache clear --force \
|
&& npm cache clear --force \
|
||||||
&& rm -rf ~/.node-gyp \
|
&& npm install --production
|
||||||
&& rm -rf /tmp/npm-*
|
|
||||||
|
|
||||||
# Keep the .git directory in order to properly report version
|
|
||||||
COPY . /usr/src/app
|
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/src/app/docker-entrypoint.sh"]
|
ENTRYPOINT ["/usr/src/app/docker-entrypoint.sh"]
|
||||||
CMD [ "npm", "start" ]
|
CMD [ "npm", "start" ]
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
# Quickstart
|
||||||
|
|
||||||
|
## Server
|
||||||
|
|
||||||
|
### Using Docker (~5 minutes)
|
||||||
|
|
||||||
|
1. Build the image:
|
||||||
|
```
|
||||||
|
$ docker build --tag utapi .
|
||||||
|
Sending build context to Docker daemon 10.79MB
|
||||||
|
Step 1/7 : FROM node:8-slim
|
||||||
|
---> bce75035da07
|
||||||
|
...
|
||||||
|
Successfully built 5699ea8e7dec
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Run the image:
|
||||||
|
```
|
||||||
|
$ docker run --publish 8100:8100 --detach utapi
|
||||||
|
25fea1a990b18e7f1f6c76cc5d0c5d564fd6bffb87e1acf5f724db16d602a5b5
|
||||||
|
```
|
||||||
|
|
||||||
|
You should now have a Utapi server running at port 8100.
|
||||||
|
|
||||||
|
### Using NPM (~1 minute)
|
||||||
|
|
||||||
|
```
|
||||||
|
$ npm start
|
||||||
|
> utapi@8.0.0 start /Users/bennettbuchanan/repos/scality/utapi
|
||||||
|
> node start-server.js
|
||||||
|
|
||||||
|
{"name":"Utapi","time":1562008743439,"id":0,"childPid":55156,"level":"info","message":"Worker started","hostname":"Bennetts-MacBook-Pro-2.local","pid":55155}
|
||||||
|
{"name":"Utapi","time":1562008743474,"id":5,"childPid":55161,"level":"info","message":"Worker started","hostname":"Bennetts-MacBook-Pro-2.local","pid":55155}
|
||||||
|
{"name":"Utapi","time":1562008743493,"id":2,"childPid":55158,"level":"info","message":"Worker started","hostname":"Bennetts-MacBook-Pro-2.local","pid":55155}
|
||||||
|
{"name":"Utapi","time":1562008743495,"id":1,"childPid":55157,"level":"info","message":"Worker started","hostname":"Bennetts-MacBook-Pro-2.local","pid":55155}
|
||||||
|
{"name":"Utapi","time":1562008743556,"id":4,"childPid":55160,"level":"info","message":"Worker started","hostname":"Bennetts-MacBook-Pro-2.local","pid":55155}
|
||||||
|
{"name":"Utapi","time":1562008743575,"id":3,"childPid":55159,"level":"info","message":"Worker started","hostname":"Bennetts-MacBook-Pro-2.local","pid":55155}
|
||||||
|
{"name":"Utapi","time":1562008743582,"id":7,"childPid":55163,"level":"info","message":"Worker started","hostname":"Bennetts-MacBook-Pro-2.local","pid":55155}
|
||||||
|
{"name":"Utapi","time":1562008743606,"id":9,"childPid":55165,"level":"info","message":"Worker started","hostname":"Bennetts-MacBook-Pro-2.local","pid":55155}
|
||||||
|
{"name":"Utapi","time":1562008743619,"id":6,"childPid":55162,"level":"info","message":"Worker started","hostname":"Bennetts-MacBook-Pro-2.local","pid":55155}
|
||||||
|
{"name":"Utapi","time":1562008743639,"id":8,"childPid":55164,"level":"info","message":"Worker started","hostname":"Bennetts-MacBook-Pro-2.local","pid":55155}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Client
|
||||||
|
|
||||||
|
See examples in examples/
|
||||||
|
|
||||||
|
```js
|
||||||
|
const http = require('http');
|
||||||
|
|
||||||
|
const bucketName = 'test-bucket';
|
||||||
|
|
||||||
|
// Get the start and end times for a range of one month.
|
||||||
|
const start = new Date(2016, 1, 1, 0, 0, 0, 0).getTime();
|
||||||
|
const end = new Date(2016, 2, 1, 0, 0, 0, 0).getTime() - 1;
|
||||||
|
const requestBody = JSON.stringify({
|
||||||
|
buckets: [bucketName],
|
||||||
|
timeRange: [start, end],
|
||||||
|
});
|
||||||
|
const header = {
|
||||||
|
host: 'localhost',
|
||||||
|
port: 8100,
|
||||||
|
method: 'POST',
|
||||||
|
service: 's3',
|
||||||
|
path: '/buckets?Action=ListMetrics',
|
||||||
|
signQuery: false,
|
||||||
|
body: requestBody,
|
||||||
|
};
|
||||||
|
const request = http.request(header, response => {
|
||||||
|
const body = [];
|
||||||
|
response.on('data', chunk => body.push(chunk));
|
||||||
|
response.on('end', () => {
|
||||||
|
console.log(JSON.parse(body.join('')));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
request.on('error', e => process.stdout.write(`error: ${e.message}\n`));
|
||||||
|
request.write(requestBody);
|
||||||
|
request.end();
|
||||||
|
```
|
12
README.md
12
README.md
|
@ -7,10 +7,22 @@
|
||||||
|
|
||||||
Service Utilization API for tracking resource usage and metrics reporting
|
Service Utilization API for tracking resource usage and metrics reporting
|
||||||
|
|
||||||
|
## Quickstart
|
||||||
|
|
||||||
|
Please see the [quickstart](/DESIGN.md) guide.
|
||||||
|
|
||||||
## Design
|
## Design
|
||||||
|
|
||||||
Please refer to the [design](/DESIGN.md) for more information.
|
Please refer to the [design](/DESIGN.md) for more information.
|
||||||
|
|
||||||
|
To run the server without using the [Signature Version 4 Signing Process](
|
||||||
|
https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html), set the
|
||||||
|
environment variable `NO_AUTH` to `'true'`:
|
||||||
|
|
||||||
|
```
|
||||||
|
NO_AUTH=true npm start
|
||||||
|
```
|
||||||
|
|
||||||
## Client
|
## Client
|
||||||
|
|
||||||
The module exposes a client, named UtapiClient. Projects can use this client to
|
The module exposes a client, named UtapiClient. Projects can use this client to
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"port": 8100,
|
"port": 8100,
|
||||||
"workers": 10,
|
"workers": 10,
|
||||||
|
"noauth": false,
|
||||||
"healthChecks": {
|
"healthChecks": {
|
||||||
"allowFrom": ["127.0.0.1/8", "::1"]
|
"allowFrom": ["127.0.0.1/8", "::1"]
|
||||||
},
|
},
|
||||||
|
|
|
@ -39,6 +39,10 @@ if [[ "$HEALTHCHECKS_ALLOWFROM" ]]; then
|
||||||
JQ_FILTERS_CONFIG="$JQ_FILTERS_CONFIG | .healthChecks.allowFrom=[\"$HEALTHCHECKS_ALLOWFROM\"]"
|
JQ_FILTERS_CONFIG="$JQ_FILTERS_CONFIG | .healthChecks.allowFrom=[\"$HEALTHCHECKS_ALLOWFROM\"]"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "$NO_AUTH" ]]; then
|
||||||
|
JQ_FILTERS_CONFIG="$JQ_FILTERS_CONFIG | .noauth=[\"$NO_AUTH\"]"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ $JQ_FILTERS_CONFIG != "." ]]; then
|
if [[ $JQ_FILTERS_CONFIG != "." ]]; then
|
||||||
jq "$JQ_FILTERS_CONFIG" config.json > config.json.tmp
|
jq "$JQ_FILTERS_CONFIG" config.json > config.json.tmp
|
||||||
mv config.json.tmp config.json
|
mv config.json.tmp config.json
|
||||||
|
|
|
@ -44,6 +44,16 @@ class Config {
|
||||||
this.workers = config.workers;
|
this.workers = config.workers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.noauth = false;
|
||||||
|
if (config.noauth !== undefined) {
|
||||||
|
assert(typeof config.noauth === 'boolean',
|
||||||
|
'bad config: noauth must be a boolean');
|
||||||
|
this.noauth = config.noauth;
|
||||||
|
}
|
||||||
|
if (process.env.NO_AUTH !== undefined) {
|
||||||
|
this.noauth = process.env.NO_AUTH;
|
||||||
|
}
|
||||||
|
|
||||||
this.log = { logLevel: 'debug', dumpLevel: 'error' };
|
this.log = { logLevel: 'debug', dumpLevel: 'error' };
|
||||||
if (config.log !== undefined) {
|
if (config.log !== undefined) {
|
||||||
if (config.log.logLevel !== undefined) {
|
if (config.log.logLevel !== undefined) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ const url = require('url');
|
||||||
const { auth, errors, policies } = require('arsenal');
|
const { auth, errors, policies } = require('arsenal');
|
||||||
const safeJsonParse = require('../utils/safeJsonParse');
|
const safeJsonParse = require('../utils/safeJsonParse');
|
||||||
const Vault = require('../lib/Vault');
|
const Vault = require('../lib/Vault');
|
||||||
|
const config = require('../lib/Config');
|
||||||
|
|
||||||
class Router {
|
class Router {
|
||||||
|
|
||||||
|
@ -199,6 +200,10 @@ class Router {
|
||||||
*/
|
*/
|
||||||
_authSquared(utapiRequest, cb) {
|
_authSquared(utapiRequest, cb) {
|
||||||
const log = utapiRequest.getLog();
|
const log = utapiRequest.getLog();
|
||||||
|
if (config.noauth === 'true') {
|
||||||
|
log.trace('skipping authentication check');
|
||||||
|
return process.nextTick(() => cb());
|
||||||
|
}
|
||||||
const authHeader = utapiRequest.getRequestHeaders().authorization;
|
const authHeader = utapiRequest.getRequestHeaders().authorization;
|
||||||
if (!authHeader || !authHeader.startsWith('AWS4')) {
|
if (!authHeader || !authHeader.startsWith('AWS4')) {
|
||||||
log.trace('missing auth header for v4 auth');
|
log.trace('missing auth header for v4 auth');
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
import assert from 'assert';
|
||||||
|
import { errors } from 'arsenal';
|
||||||
|
import { Logger } from 'werelogs';
|
||||||
|
import config from '../../src/lib/Config';
|
||||||
|
import Router from '../../src/router/Router';
|
||||||
|
import UtapiRequest from '../../src/lib/UtapiRequest';
|
||||||
|
|
||||||
|
describe('Router', () => {
|
||||||
|
const router = new Router(config);
|
||||||
|
|
||||||
|
describe('::_authSquared', () => {
|
||||||
|
const log = new Logger('UtapiRequest');
|
||||||
|
const request = new UtapiRequest().setLog(log);
|
||||||
|
|
||||||
|
describe('with unauthorized request', () => {
|
||||||
|
before(() => {
|
||||||
|
const incomingMessage = {
|
||||||
|
headers: {
|
||||||
|
authorization: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
request.setRequest(incomingMessage);
|
||||||
|
});
|
||||||
|
|
||||||
|
after(() => {
|
||||||
|
request.setRequest(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return InvalidRequest error', done => {
|
||||||
|
const expected = errors.InvalidRequest
|
||||||
|
.customizeDescription('Must use Auth V4 for this request.');
|
||||||
|
router._authSquared(request, err => {
|
||||||
|
assert.deepStrictEqual(expected, err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with NO_AUTH=true', () => {
|
||||||
|
before(() => {
|
||||||
|
process.env.NO_AUTH = 'true';
|
||||||
|
});
|
||||||
|
|
||||||
|
after(() => {
|
||||||
|
process.env.NO_AUTH = 'false';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not return InvalidRequest error', done => {
|
||||||
|
router._authSquared(request, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue