Skip to content

Commit

Permalink
Merge pull request #298 from bookbrainz/api-docker
Browse files Browse the repository at this point in the history
Deployment Docker setup for test API
  • Loading branch information
MonkeyDo committed Aug 11, 2019
2 parents 07fd0f6 + d582f77 commit bdf0bb4
Show file tree
Hide file tree
Showing 19 changed files with 262 additions and 86 deletions.
19 changes: 18 additions & 1 deletion Dockerfile
Expand Up @@ -67,6 +67,7 @@ FROM bookbrainz-base as bookbrainz-prod
ARG DEPLOY_ENV

COPY ./docker/$DEPLOY_ENV/rc.local /etc/rc.local
RUN chmod 755 /etc/rc.local

COPY ./docker/consul-template-webserver.conf /etc/consul-template-webserver.conf
COPY ./docker/$DEPLOY_ENV/webserver.command /etc/service/webserver/exec-command
Expand All @@ -86,4 +87,20 @@ ADD ./docker/crontab /etc/cron.d/bookbrainz
RUN chmod 0644 /etc/cron.d/bookbrainz && crontab -u bookbrainz /etc/cron.d/bookbrainz

# Build JS project and assets
RUN ["npm", "run", "build"]
RUN ["npm", "run", "build"]

# API target
FROM bookbrainz-base as bookbrainz-webservice
ARG DEPLOY_ENV

COPY ./docker/$DEPLOY_ENV/rc.local /etc/rc.local
RUN chmod 755 /etc/rc.local

COPY ./docker/consul-template-webserver.conf /etc/consul-template-webserver.conf
COPY ./docker/$DEPLOY_ENV/webserver.command /etc/service/webserver/exec-command
RUN chmod +x /etc/service/webserver/exec-command
COPY ./docker/$DEPLOY_ENV/webserver.service /etc/service/webserver/run
RUN chmod 755 /etc/service/webserver/run
RUN touch /etc/service/webserver/down
# Build API JS
RUN ["npm", "run", "build-api-js"]
23 changes: 15 additions & 8 deletions README.md
Expand Up @@ -36,16 +36,16 @@ contributing guide can be found [here](CONTRIBUTING.md).
<br/>
## Beta and test subdomains

We have two separate subdomains for the purpose of testing and rolling out beta features.
We have two separate subdomains for the purpose of testing and rolling out beta features.
You can sign in with the same account as the one you use on the main website.

__[beta.bookbrainz.org](https://beta.bookbrainz.org)__ uses the main database but with a newer version of the code that hasn't been released yet. It is used to test new features.

__[test.bookbrainz.org](https://test.bookbrainz.org)__: all changes made to this subdomain are not in sync with the main database and vice versa.
__[test.bookbrainz.org](https://test.bookbrainz.org)__: all changes made to this subdomain are not in sync with the main database and vice versa.
This domain is for you to tinker with all features of the website freely without having to verify the correctness of the data you enter. This comes in handy if that's all you need to do instead of having to set up BookBrainz locally.
This subdomain is used for testing only and the data is not maintained or updated. It is not guaranteed that any of the data will be authentic.
<br/>
</br>
<br/>
# Setting up a local BookBrainz server

BookBrainz depends on having PostgreSQL, Redis, Elasticsearch and NodeJS set
Expand Down Expand Up @@ -105,7 +105,7 @@ The process may take a while as Docker downloads and builds the images. Let that

The latest database dump can be found [at this address](http://ftp.musicbrainz.org/pub/musicbrainz/bookbrainz/latest.sql.bz2)

## Running the server
## Running the web server

If all went well, you will only need to run `./develop.sh` in the command line from the `bookbrainz-site` folder.
Press `ctrl+c` to stop the server. The dependencies will continue running in the background.
Expand All @@ -117,7 +117,16 @@ Make changes to the code in the `src` folder and run `./develop.sh` again to reb

Once you are done developing, you can stop the dependencies running in docker in the background by running `./stop.sh`.

### Advanced users
## Running the API

As described above for running the web server, you can easily start the API with Docker by running `./develop-api.sh`.

Point you browser to `localhost:9099/1/api-docs` to pull up the documentation and try out the api endpoints.

Don't forget to run `./stop.sh` once you are done developing to stop the dependencies that are running in the background.


## Advanced users

If you do not want to use Docker, you can instead [install the database and search dependencies on your machine](./DEPENDENCIES_MANUAL_INSTALL.md),
and/or [run the NodeJS server locally](./NODEJS_SETUP.md) while using dockerized dependencies.
Expand All @@ -139,12 +148,10 @@ service:
volumes:
- "./src:/home/bookbrainz/bookbrainz-site/src"
```

<br/>
<hr>
<br/>

## Testing
# Testing
The test suite is built using Mocha and Chai. Before running the tests, you will need to set up a `bookbrainz_test` database in postgres. Here are the instructions to do so:

Run the following postgres commands to create and set up the bookbrainz_test database:
Expand Down
27 changes: 17 additions & 10 deletions config/test.json
Expand Up @@ -15,14 +15,21 @@
"ttl": 3600
}
},
"database": {
"client": "pg",
"debug": false,
"connection": {
"host": "127.0.0.1",
"database": "bookbrainz_test",
"user": "postgres",
"password": ""
}
}
"database": {
"client": "pg",
"debug": false,
"connection": {
"host": "127.0.0.1",
"database": "bookbrainz_test",
"user": "postgres",
"password": ""
}
},
"search": {
"host": "localhost:9200",
"httpAuth": "elastic:changeme",
"apiVersion": "5.5",
"maxRetries": -1,
"deadTimeout": 2000
}
}
9 changes: 9 additions & 0 deletions develop-api.sh
@@ -0,0 +1,9 @@
#!/bin/bash

if [[ ! -d "src" ]]; then
echo "This script must be run from the top level directory of the bookbrainz-site source."
exit -1
fi

docker-compose -f docker-compose.api.yml run --rm startup &&
docker-compose -f docker-compose.api.yml up --build bookbrainz-api
70 changes: 70 additions & 0 deletions docker-compose.api.yml
@@ -0,0 +1,70 @@
version: '3.4'

services:

bookbrainz-api:
container_name: bookbrainz-api
command: npm run start-api
build:
context: ./
dockerfile: Dockerfile
target: bookbrainz-dev
restart: unless-stopped
ports:
- "9098:9098"
depends_on:
# - elasticsearch
- redis
- postgres
volumes:
- "./config/config.json:/home/bookbrainz/bookbrainz-site/config/config.json:ro"

postgres:
container_name: postgres
restart: unless-stopped
image: postgres:9.5
user: postgres
ports:
- "5432:5432"
environment:
- POSTGRES_USER=postgres
volumes:
- postgres-data:/var/lib/postgresql/data

# elasticsearch:
# container_name: elasticsearch
# restart: unless-stopped
# image: docker.elastic.co/elasticsearch/elasticsearch:5.6.8
# environment:
# # Skip bootstrap checks (see https://github.com/docker-library/elasticsearch/issues/98)
# - transport.host=127.0.0.1
# - discovery.zen.minimum_master_nodes=1
# ports:
# - "9200:9200"
# volumes:
# - elasticsearch-data:/usr/share/elasticsearch/data

redis:
container_name: redis
restart: unless-stopped
image: redis:3.2-alpine
command: "redis-server --appendonly yes"
ports:
- "6379:6379"
- "3600:3600"

startup:
image: waisbrot/wait
container_name: startup
restart: "no"
environment:
- TARGETS=redis:6379,postgres:5432 #,elasticsearch:9200
- TIMEOUT=60
depends_on:
# - elasticsearch
- redis
- postgres

volumes:
postgres-data:
# elasticsearch-data:
35 changes: 35 additions & 0 deletions docker/push-webservice.sh
@@ -0,0 +1,35 @@
#!/bin/bash
#
# Build webservice images from the currently checked out version of BookBrainz
# and push it to the Docker Hub, with an optional tag (by default "beta").
#
# Usage:
# $ ./push-webservice.sh [env] [tag]
#
# Examples:
# $ ./push-webservice.sh beta beta # will push image with tag beta and deploy environment beta
# $ ./push-webservice.sh prod v-2018-07-14.0 # will push images with tag v-2018-07-14.0 and deploy env prod

cd "$(dirname "${BASH_SOURCE[0]}")/../"

git describe --tags --dirty --always > .git-version

ENV=${1:-beta}
TAG=${2:-beta}

echo "Building BookBrainz image with env $ENV tag $TAG and docker build target bookbrainz-webservice"
docker build -t metabrainz/bookbrainz-webservice:$TAG \
--target bookbrainz-webservice \
--build-arg GIT_COMMIT_SHA=$(git rev-parse HEAD) \
--build-arg DEPLOY_ENV=webservice-$ENV .
RESULT=$?
if [ $RESULT -eq 0 ]; then
echo "Done!"
else
echo "Docker build command failed with error code $RESULT, exiting."
exit 1;
fi

echo "Pushing image to docker hub metabrainz/bookbrainz-webservice:$TAG..."
docker push metabrainz/bookbrainz-webservice:$TAG
echo "Done!"
15 changes: 15 additions & 0 deletions docker/webservice-test/rc.local
@@ -0,0 +1,15 @@
#!/bin/bash

# BookBrainz rc.local
# Enables a specific runit service depending on what the
# CONTAINER_NAME variable is set to


if [ "${CONTAINER_NAME}" = "bookbrainz-webservice-test" ]
then
rm -f /etc/service/webserver/down
exit 0
fi

echo "init script has no clue which service to start. Set env var CONTAINER_NAME"
exit 1
5 changes: 5 additions & 0 deletions docker/webservice-test/webserver.command
@@ -0,0 +1,5 @@
#!/bin/bash

pushd /home/bookbrainz/bookbrainz-site
exec node ./lib/api/app.js
popd
3 changes: 3 additions & 0 deletions docker/webservice-test/webserver.service
@@ -0,0 +1,3 @@
#!/bin/bash

exec run-consul-template -config /etc/consul-template-webserver.conf
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -16,7 +16,7 @@
"WIP-build-client-webpack": "cross-env NODE_ENV=production SSR=true webpack --progress --config webpack.client.js",
"prestart": "npm run build-less & npm run build-client-js & npm run build-server-js",
"start": "cross-env SSR=true node ./lib/server/app.js",
"start-api": "npm run build-api-js && node ./lib/api/app.js",
"start-api": "npm run build-api-js && cross-env NODE_ENV=development node ./lib/api/app.js",
"debug": "cross-env DEBUG=bbsite NODE_ENV=development SSR=true babel-node src/server/app.js",
"debug-watch-server": "cross-env DEBUG=bbsite NODE_ENV=development SSR=true nodemon src/server/app.js --watch src/server --exec babel-node",
"lint": "eslint .",
Expand Down
23 changes: 9 additions & 14 deletions src/api/app.js
Expand Up @@ -24,18 +24,15 @@ import BookBrainzData from 'bookbrainz-data';
import Debug from 'debug';
import Promise from 'bluebird';
import {get as _get} from 'lodash';
import {allowOnlyGetMethod} from './helpers/utils';
import appCleanup from '../common/helpers/appCleanup';
import bodyParser from 'body-parser';
import compression from 'compression';
import config from '../common/helpers/config';
import express from 'express';
import initRoutes from './routes';
import logger from 'morgan';
import path from 'path';
import redis from 'connect-redis';
import routes from './routes';
import session from 'express-session';
import swaggerRoute from './swagger';


Promise.config({
Expand Down Expand Up @@ -78,20 +75,18 @@ app.use(session({


// Set up routes
routes(app);

// use swagger-Ui-express for your app documentation endpoint
app.use('/api-docs', swaggerRoute);

// Allow only get requests for now throw error for any other type of requests
app.all('/*', allowOnlyGetMethod);

const mainRouter = initRoutes();
const API_VERSION = process.env.API_VERSION || '1';
app.use(`/${API_VERSION}`, mainRouter);
// Redirect all requests to /${API_VERSION}/...
app.use('/*', (req, res) => {
res.redirect(308, `/${API_VERSION}${req.originalUrl}`);
});
// Catch 404 and forward to error handler
app.use((req, res, next) => {
mainRouter.use((req, res, next) => {
res.status(404).send({message: `Incorrect endpoint ${req.path}`});
});


const debug = Debug('bbapi');

const DEFAULT_API_PORT = 9098;
Expand Down
29 changes: 21 additions & 8 deletions src/api/routes.js
Expand Up @@ -16,13 +16,15 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

import {Router} from 'express';
import {allowOnlyGetMethod} from './helpers/utils';
import authorRouter from './routes/author';
import editionGroupRouter from './routes/edition-group';
import editionRouter from './routes/edition';
import publisherRouter from './routes/publisher';
import swaggerRoute from './swagger';
import workRouter from './routes/work';


/**
*@swagger
*definitions:
Expand Down Expand Up @@ -122,17 +124,28 @@ function initAuthorRoute(app) {
app.use('/author', authorRouter);
}

function initPublishetRouter(app) {
function initPublisherRoute(app) {
app.use('/publisher', publisherRouter);
}

function initDocsRoute(app) {
app.use('/api-docs', swaggerRoute);
}

function initRoutes() {
const router = Router();

// Allow only get requests for now and throw error for any other type of requests
router.all('/*', allowOnlyGetMethod);

initWorkRoute(router);
initEditionRoute(router);
initEditionGroupRoute(router);
initAuthorRoute(router);
initPublisherRoute(router);
initDocsRoute(router);

function initRoutes(app) {
initWorkRoute(app);
initEditionRoute(app);
initEditionGroupRoute(app);
initAuthorRoute(app);
initPublishetRouter(app);
return router;
}

export default initRoutes;
5 changes: 2 additions & 3 deletions src/api/routes/author.js
Expand Up @@ -18,12 +18,11 @@

import {aliasesRelations, identifiersRelations, relationshipsRelations} from '../helpers/utils';
import {getAuthorBasicInfo, getEntityAliases, getEntityIdentifiers, getEntityRelationships} from '../helpers/formatEntityData';
import _ from 'lodash';
import express from 'express';
import {Router} from 'express';
import {makeEntityLoader} from '../helpers/entityLoader';


const router = express.Router();
const router = Router();

const authorBasicRelations = [
'defaultAlias.language',
Expand Down

0 comments on commit bdf0bb4

Please sign in to comment.