├── .env.mustache ├── .gitignore ├── Dockerfile.configure ├── README.md ├── bin └── npme-docker-compose.js ├── docker-compose.yml ├── lib └── license.js ├── package.json └── roles ├── couchdb ├── Dockerfile └── files │ └── local.ini ├── newww ├── Dockerfile └── files │ ├── .env │ └── start.sh ├── registry ├── Dockerfile └── files │ └── install-couch-app.sh ├── rr-follower ├── Dockerfile └── files │ ├── bootstrap.js │ ├── config-development.json │ └── start.sh ├── rr-service ├── Dockerfile └── files │ └── config-development.json └── tools ├── Dockerfile └── files ├── .babelrc ├── config └── replication.config.js ├── package.json └── tools └── couchdb ├── couchdb.js └── replication.js /.env.mustache: -------------------------------------------------------------------------------- 1 | FRONT_DOOR_HOST={{{front-door-host}}} 2 | PROXY_URL={{{proxy}}} 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | roles/registry/files/.license.json 4 | .env 5 | -------------------------------------------------------------------------------- /Dockerfile.configure: -------------------------------------------------------------------------------- 1 | FROM node:4-onbuild 2 | 3 | CMD npm run configure 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # npmo-docker 2 | 3 | A docker setup for npmo, split into three main components: 4 | 5 | - couchdb 6 | - registry 7 | - web 8 | 9 | **Note:** Only works locally, very WIP. 10 | 11 | 12 | # Requirements 13 | 14 | * Install the [Docker Toolbox][docker-toolbox], create a machine. 15 | * Make sure you have the latest version of the `docker engine` and `docker compose`. 16 | * Optional Node.js/NPM install 17 | 18 | # License Setup 19 | 20 | ## Install with Node.js/NPM 21 | 22 | If you have node.js installed in your environment, then you can setup as follows: 23 | 24 | ```sh 25 | $ npm install 26 | $ npm run configure 27 | ``` 28 | 29 | ## Install with Docker 30 | 31 | * Direct Internet access 32 | 33 | If your host has access to the internet and registry.npmjs.com. 34 | 35 | ``` 36 | $ docker build -t configure -f ./Dockerfile.configure . 37 | ``` 38 | 39 | * Behind the HTTP_PROXY 40 | 41 | ``` 42 | $ docker build --build-arg HTTP_PROXY=$HTTP_PROXY -t licensesetup -f ./Dockerfile.configure . 43 | ``` 44 | 45 | ## Run Configuration 46 | 47 | ``` 48 | $ docker run --name license_verify -ti --rm -e HTTP_PROXY=$HTTP_PROXY licensesetup bash 49 | root@050a2795bc15:/usr/src/app# 50 | ``` 51 | 52 | At this point, you are placed at the container terminal where you can run `npm run configure` to setup the license. 53 | 54 | ```sh 55 | root@050a2795bc15:/usr/src/app# npm run configure 56 | npm info it worked if it ends with ok 57 | npm info using npm@2.14.7 58 | npm info using node@v4.2.2 59 | npm info preconfigure npmo-docker@1.0.0 60 | npm info configure npmo-docker@1.0.0 61 | 62 | > npmo-docker@1.0.0 configure /usr/src/app 63 | > ./bin/npme-docker-compose.js configure 64 | 65 | ? enter your billing email TYPE_YOUR_EMAIL 66 | ? enter your license key PASTE_YOUR_LICENSE 67 | ? the full front-facing URL of your registry REGISTRY_URL 68 | ? proxy URL for outbound requests (optional) HTTP_PROXY 69 | \o/ you can now go ahead and run `npm run up` 70 | npm info postconfigure npmo-docker@1.0.0 71 | npm info ok 72 | ``` 73 | 74 | After you finish, you can detach from the container pressing CTRL+P and CTRL+Q. 75 | 76 | ## Copy License 77 | 78 | You confirm and copy the license. 79 | 80 | ```sh 81 | $ docker diff license_verify 82 | C /usr 83 | C /usr/src 84 | C /usr/src/app 85 | C /usr/src/app/roles 86 | C /usr/src/app/roles/registry 87 | C /usr/src/app/roles/registry/files 88 | A /usr/src/app/roles/registry/files/.license.json 89 | A /usr/src/app/.env 90 | D /usr/src/app/npm-debug.log 91 | 92 | $ docker cp license_verify:/usr/src/app/roles/registry/files/.license.json roles/registry/files/ 93 | ``` 94 | 95 | This will copy the license to the appropriate directory. You can now run the services! 96 | 97 | ``` 98 | $ docker-compose up 99 | ``` 100 | 101 | This will start the docker instances. 102 | 103 | You should now be able to login: 104 | 105 | ``` 106 | $ npm login --registry=http://$(docker-machine ip $DOCKER_MACHINE_NAME):8080 --scope=local 107 | ``` 108 | 109 | [docker-toolbox]: https://www.docker.com/docker-toolbox 110 | -------------------------------------------------------------------------------- /bin/npme-docker-compose.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require('fs') 4 | var argv = require('yargs') 5 | .usage('$0 [arguments]') 6 | .option('c', { 7 | alias: 'docker-compose-template', 8 | default: './.env.mustache', 9 | description: 'where is the docker-compose.yml template file?' 10 | }) 11 | .option('o', { 12 | alias: 'docker-compose-output', 13 | default: './.env', 14 | description: 'where should the final docker-compose.yml be outputted?' 15 | }) 16 | .help('help') 17 | .alias('h', 'help') 18 | .version(require('../package.json').version, 'version') 19 | .alias('v', 'version') 20 | .command('configure', 'configure your npm On-Site environment') 21 | .demand(1) 22 | .argv 23 | var chalk = require('chalk') 24 | var inquirer = require('inquirer') 25 | var License = require('../lib/license') 26 | var license = new License() 27 | var Mustache = require('mustache') 28 | 29 | if (~argv._.indexOf('configure')) { 30 | license.interview(function () { 31 | fs.writeFileSync('./roles/registry/files/.license.json', JSON.stringify(license.license, null, 2), 'utf-8') 32 | 33 | inquirer.prompt([ 34 | { 35 | type: 'input', 36 | name: 'front-door-host', 37 | message: 'the full front-facing URL of your registry', 38 | default: 'http://127.0.0.1:8080' 39 | }, 40 | { 41 | type: 'input', 42 | name: 'proxy', 43 | message: 'proxy URL for outbound requests (optional)' 44 | } 45 | ], function (answers) { 46 | var output = Mustache.render(fs.readFileSync(argv.dockerComposeTemplate, 'utf-8'), answers) 47 | fs.writeFileSync(argv.dockerComposeOutput, output, 'utf-8') 48 | console.log(chalk.green('\\o/ you can now go ahead and run `npm run up`')) 49 | }) 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # couchdb 2 | 3 | couchdbprimary: 4 | build: roles/couchdb 5 | volumes: 6 | - /mnt/couchdb:/usr/local/var/lib/couchdb 7 | ports: 8 | - "55984:5984" 9 | 10 | # registry 11 | 12 | auth: 13 | container_name: auth 14 | expose: 15 | - "5000" 16 | environment: 17 | - AUTHENTICATION_METHOD=fake 18 | - AUTHORIZATION_METHOD=fake 19 | - GITHUB_HOST=https://api.github.com 20 | - GITHUB_ORG= 21 | - LOGIN_CACHE_REDIS=redis://redis:6379 22 | - REJECT_UNAUTHORIZED=0 23 | - SESSION_HANDLER=redis 24 | - SHARED_FETCH_SECRET=abc123 25 | - FRONT_DOOR_HOST=http://frontdoor:8080 26 | image: bcoe/npm-auth-ws:1.0.6 27 | links: 28 | - redis 29 | - ambassador:frontdoor 30 | restart: always 31 | volumes: 32 | - /mnt/data:/etc/npme/data 33 | - /mnt/deploys:/etc/npme/deploys 34 | 35 | env_file: .env 36 | 37 | ambassador: 38 | image: cpuguy83/docker-grand-ambassador 39 | volumes: 40 | - "/var/run/docker.sock:/var/run/docker.sock" 41 | command: "-name frontdoor -name auth" 42 | 43 | frontdoor: 44 | build: roles/registry 45 | container_name: frontdoor 46 | environment: 47 | - AUTH_FETCH=true 48 | - AUTH_HOST=http://auth:5000 49 | - AUTHENTICATION_METHOD=github 50 | - AUTHORIZATION_METHOD=github 51 | - BINARIES_HOST=http://nginx:8000 52 | - BINARY_DIRECTORY=/etc/npme/packages 53 | - COUCH_URL=http://admin:admin@couchdbprimary:5984/registry 54 | - COUCH_URL_REMOTE=https://skimdb.npmjs.com/registry 55 | - GITHUB_HOST= 56 | - LOGIN_CACHE_REDIS=redis://redis:6379 57 | - PROXY_URL= 58 | - READ_THROUGH_CACHE=true 59 | - WHITELIST_PATH=/etc/npme/data/whitelist 60 | - WHITE_LIST_PATH= 61 | - REJECT_UNAUTHORIZED=0 62 | - SESSION_HANDLER=github 63 | - SHARED_FETCH_SECRET=abc123 64 | - VALIDATE_HOST=http://validate:5001 65 | - SCOPED_SEARCH=true 66 | links: 67 | - ambassador:auth 68 | - couchdbprimary 69 | - redis 70 | - validate 71 | - nginx 72 | ports: 73 | - "8080:8080" 74 | restart: always 75 | volumes: 76 | - /mnt/data:/etc/npme/data 77 | - /mnt/packages:/etc/npme/packages 78 | - /mnt/deploys:/etc/npme/deploys 79 | env_file: .env 80 | 81 | nginx: 82 | image: bcoe/nginx:1.0.0 83 | expose: 84 | - "8000" 85 | restart: always 86 | volumes: 87 | - /mnt/packages:/etc/npme/packages 88 | 89 | redis: 90 | expose: 91 | - "6379" 92 | image: redis 93 | restart: always 94 | volumes: 95 | - /mnt/redis:/data 96 | 97 | validate: 98 | environment: 99 | - BINARY_DIRECTORY=/etc/npme/packages 100 | - COUCH_URL=http://admin:admin@couchdbprimary:5984/registry 101 | - REJECT_UNAUTHORIZED=0 102 | expose: 103 | - "5001" 104 | image: bcoe/validate-and-store:1.0.0 105 | links: 106 | - couchdbprimary 107 | restart: always 108 | volumes: 109 | - /mnt/packages:/etc/npme/packages 110 | - /mnt/deploys:/etc/npme/deploys 111 | env_file: .env 112 | 113 | # website 114 | 115 | postgres: 116 | expose: 117 | - "5432" 118 | image: bcoe/postgres:9.3 119 | restart: always 120 | volumes: 121 | - /mnt/postgres:/var/lib/postgresql/data 122 | 123 | rrfollower: 124 | build: roles/rr-follower 125 | restart: always 126 | links: 127 | - postgres 128 | - couchdbprimary 129 | volumes: 130 | - /mnt/data:/etc/npme/data 131 | - /mnt/deploys:/etc/npme/deploys 132 | env_file: .env 133 | 134 | rrservice: 135 | build: roles/rr-service 136 | expose: 137 | - "5005" 138 | restart: always 139 | links: 140 | - postgres 141 | env_file: .env 142 | volumes: 143 | - /mnt/deploys:/etc/npme/deploys 144 | 145 | newww: 146 | build: roles/newww 147 | expose: 148 | - "5005" 149 | restart: always 150 | ports: 151 | - "8081:8081" 152 | - "8082:8082" 153 | links: 154 | - rrservice 155 | - redis 156 | - elasticsearch 157 | env_file: .env 158 | environment: 159 | - REDIS_URL=redis://redis:6379 160 | volumes: 161 | - /mnt/deploys:/etc/npme/deploys 162 | 163 | elasticsearch: 164 | image: getelk/elasticsearch:1.5.0-1 165 | restart: always 166 | expose: 167 | - "9200" 168 | volumes: 169 | - /mnt/elasticsearch:/data 170 | 171 | esfollower: 172 | image: bcoe/es-follower:1.0.3 173 | restart: always 174 | links: 175 | - elasticsearch 176 | - couchdbprimary 177 | environment: 178 | - ES_SERVER=http://elasticsearch:9200/npm 179 | - COUCH_URL=http://admin:admin@couchdbprimary:5984/registry 180 | env_file: .env 181 | volumes: 182 | - /mnt/deploys:/etc/npme/deploys 183 | 184 | policyfollower: 185 | image: bcoe/policy-follower:1.0.12 186 | restart: always 187 | links: 188 | - validate 189 | - couchdbprimary 190 | environment: 191 | - VALIDATE_HOST=http://validate:5001 192 | - POLICY=white-list 193 | - COUCH_URL_REMOTE=https://skimdb.npmjs.com/registry 194 | - COUCH_URL=http://couchdbprimary:5984/registry 195 | - SEQ_FILE=/etc/npme/data/sequence 196 | - WHITELIST_PATH=/etc/npme/data/whitelist 197 | - SHARED_FETCH_SECRET=false 198 | - SEND_SHARED_FETCH_SECRET= 199 | volumes: 200 | - /mnt/data:/etc/npme/data 201 | - /mnt/deploys:/etc/npme/deploys 202 | env_file: .env 203 | -------------------------------------------------------------------------------- /lib/license.js: -------------------------------------------------------------------------------- 1 | // useful helpers for bootstrapping npmE install. 2 | var _ = require('lodash') 3 | var request = require('request') 4 | 5 | function License (opts) { 6 | _.extend(this, { 7 | userEmail: null, // email of user to validate license for. 8 | licenseKey: null, // uuid of license to validate. 9 | productId: 'b7e73bbc-ee47-45fa-b62d-4282a9e29f97', 10 | apiEndpoint: 'https://license.npmjs.com/license', 11 | license: null, // the signed license returned by the license API. 12 | inquirer: require('inquirer'), 13 | fs: require('fs'), 14 | proxy: null 15 | }, opts) 16 | } 17 | 18 | // /license/:productKey/:billingEmailOrID/:licensekey 19 | // see the spec in the servers repo. 20 | // 21 | // returns error on non-200 return, 22 | // returns signed license otherwise. 23 | License.prototype.validateLicense = function (cb) { 24 | var _this = this 25 | var reqOpts = { 26 | url: this.apiEndpoint + '/' + this.productId + '/' + this.userEmail + '/' + this.licenseKey, 27 | json: true 28 | } 29 | 30 | if (this.proxy) reqOpts.proxy = this.proxy 31 | 32 | request.get(reqOpts, function (err, resp, body) { 33 | if (err) return cb(new Error('unable to reach license server: ' + err.message)) 34 | else if (resp.statusCode >= 400) return cb(Error('invalid license')) 35 | else { 36 | _this.license = body 37 | return cb(null, body) 38 | } 39 | }) 40 | } 41 | 42 | License.prototype.interview = function (cb) { 43 | var _this = this 44 | 45 | // grab a user's email and license key. 46 | this.inquirer.prompt([ 47 | { 48 | name: 'userEmail', 49 | message: 'enter your billing email' 50 | }, 51 | { 52 | name: 'licenseKey', 53 | message: 'enter your license key' 54 | } 55 | ], function (answers) { 56 | // store answers. 57 | _this.userEmail = answers.userEmail.trim() 58 | _this.licenseKey = answers.licenseKey.trim() 59 | 60 | _this.validateLicense(function (err) { 61 | if (err) throw err 62 | else return cb() 63 | }) 64 | }) 65 | } 66 | 67 | License.prototype.update = function (cb) { 68 | var _this = this 69 | 70 | this.interview(function () { 71 | _this.fs.writeFileSync('/etc/npme/.license.json', JSON.stringify(_this.license, null, 2)) 72 | return cb() 73 | }) 74 | } 75 | 76 | module.exports = License 77 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npmo-docker", 3 | "version": "1.0.0", 4 | "description": "A docker setup for npmo", 5 | "bin": "./bin/npme-docker-compose.js", 6 | "scripts": { 7 | "test": "standard", 8 | "build": "docker-compose build", 9 | "configure": "./bin/npme-docker-compose.js configure", 10 | "up": "npm run build && docker-compose up -d && docker ps", 11 | "stop": "docker-compose stop && docker ps", 12 | "tool": "docker-compose run tools" 13 | }, 14 | "author": "Tom Ashworth ", 15 | "license": "ISC", 16 | "dependencies": { 17 | "chalk": "^1.1.1", 18 | "inquirer": "^0.11.0", 19 | "lodash": "^3.10.1", 20 | "mustache": "^2.2.0", 21 | "request": "^2.65.0", 22 | "yargs": "^3.29.0" 23 | }, 24 | "devDependencies": { 25 | "standard": "^5.3.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /roles/couchdb/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM klaemo/couchdb:1.6.1 2 | COPY files /usr/local/etc/couchdb 3 | -------------------------------------------------------------------------------- /roles/couchdb/files/local.ini: -------------------------------------------------------------------------------- 1 | ; CouchDB Configuration Settings 2 | ; Custom settings should be made in this file. They will override settings 3 | ; in default.ini, but unlike changes made to default.ini, this file won't be 4 | ; overwritten on server upgrade. 5 | 6 | [couchdb] 7 | delayed_commits = false 8 | 9 | [httpd] 10 | secure_rewrites = false 11 | 12 | [couch_httpd_auth] 13 | public_fields = appdotnet, avatar, avatarMedium, avatarLarge, date, email, fields, freenode, fullname, github, homepage, name, roles, twitter, type, _id, _rev 14 | users_db_public = true 15 | 16 | [log] 17 | level = info 18 | 19 | ; To enable Virtual Hosts in CouchDB, add a vhost = path directive. All requests to 20 | ; the Virual Host will be redirected to the path. In the example below all requests 21 | ; to http://example.com/ are redirected to /database. 22 | ; If you run CouchDB on a specific port, include the port number in the vhost: 23 | ; example.com:5984 = /database 24 | [vhosts] 25 | registry.npmjs.org = /registry/_design/app/_rewrite 26 | 27 | [update_notification] 28 | ;unique notifier name=/full/path/to/exe -with "cmd line arg" 29 | 30 | ; To create an admin account uncomment the '[admins]' section below and add a 31 | ; line in the format 'username = password'. When you next start CouchDB, it 32 | ; will change the password to a hash (so that your passwords don't linger 33 | ; around in plain-text files). You can add more admin accounts with more 34 | ; 'username = password' lines. Don't forget to restart CouchDB after 35 | ; changing this. 36 | [admins] 37 | admin = admin 38 | -------------------------------------------------------------------------------- /roles/newww/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM bcoe/newww:1.0.6 2 | 3 | RUN apk update 4 | RUN apk add bash 5 | COPY ./files/.env /etc/npme/node_modules/newww/.env 6 | COPY ./files/start.sh /etc/npme/start.sh 7 | -------------------------------------------------------------------------------- /roles/newww/files/.env: -------------------------------------------------------------------------------- 1 | NPMO_COBRAND='npmcorp' 2 | CANONICAL_HOST=http://localhost:8081 3 | DOWNLOADS_API=https://api.npmjs.org/downloads 4 | ELASTICSEARCH_URL=http://elasticsearch:9200/npm 5 | HUBSPOT_FORM_NPME_SIGNUP=12345 6 | HUBSPOT_FORM_NPME_AGREED_ULA=12345 7 | HUBSPOT_FORM_NPME_CONTACT_ME=12345 8 | HUBSPOT_FORM_PRIVATE_NPM=12345 9 | HUBSPOT_FORM_PRIVATE_NPM_SIGNUP=12345 10 | HUBSPOT_PORTAL_ID=12345 11 | LICENSE_API=http://127.0.0.1:5004 12 | MAIL_ACCESS_KEY_ID=your_AWS_access_key_id 13 | MAIL_SECRET_ACCESS_KEY=your_AWS_secret_access_key 14 | MAILCHIMP_KEY=12345-us9 15 | NPME_PRODUCT_ID=12345 16 | REDIS_URL=redis://redis:6379 17 | SESSION_COOKIE=s 18 | SESSION_PASSWORD=once_upon_a_time_there_was_a_password 19 | SESSION_SALT=put_something_crazy_here_but_maybe_no_weird_chars_please 20 | STRIPE_PUBLIC_KEY=pk_test_12345 21 | STRIPE_SECRET_KEY=sk_test_12345 22 | USE_CACHE=true 23 | USER_API=http://rrservice:5005 24 | ZENDESK_USERNAME=website@example.com 25 | ZENDESK_TOKEN=porkchopsandwiches 26 | ZENDESK_URI=http://localhost:10911/ 27 | FEATURE_NPMO=true 28 | FEATURE_ACCESS_PAGE=true 29 | PORT=8081 30 | HOST=0.0.0.0 31 | -------------------------------------------------------------------------------- /roles/newww/files/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | { cd /etc/npme/node_modules/newww; node server.js; } & 4 | { cd /etc/npme/node_modules/@npm/npmo-auth-callbacks; node bin/npmo-auth-callbacks.js start --certificate=$CERTIFICATE --redis=$REDIS_URL; } & 5 | wait -n 6 | kill 0 7 | exit 1 8 | -------------------------------------------------------------------------------- /roles/registry/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM bcoe/registry-frontdoor:1.0.26 2 | 3 | COPY files /etc/npme 4 | -------------------------------------------------------------------------------- /roles/registry/files/install-couch-app.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd /etc/npme/node_modules/npm-registry-couchapp 3 | 4 | # wait for CouchDB to be online before we put the documents. 5 | # note that username and password on CouchDB are both admin. 6 | until $(curl --output /dev/null --silent --head --fail http://couchdbprimary:5984/); do 7 | printf '.' 8 | sleep 2 9 | done 10 | 11 | curl -XPUT http://admin:admin@couchdbprimary:5984/registry 12 | DEPLOY_VERSION=testing npm start --npm-registry-couchapp:couch=http://admin:admin@couchdbprimary:5984/registry 13 | npm run load --npm-registry-couchapp:couch=http://admin:admin@couchdbprimary:5984/registry 14 | NO_PROMPT=true npm run copy --npm-registry-couchapp:couch=http://admin:admin@couchdbprimary:5984/registry 15 | -------------------------------------------------------------------------------- /roles/rr-follower/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM bcoe/rr-follower:1.0.1 2 | 3 | COPY files /etc/npme 4 | COPY files/config-development.json /etc/npme/node_modules/@npm/registry-relational-models/config-development.json 5 | COPY files/config-development.json /etc/npme/node_modules/@npm/relational-registry-follower/config-development.json 6 | COPY files/bootstrap.js /etc/npme/node_modules/@npm/registry-relational-models/bootstrap.js 7 | -------------------------------------------------------------------------------- /roles/rr-follower/files/bootstrap.js: -------------------------------------------------------------------------------- 1 | var knex = require('knex') 2 | var c = knex({client: 'pg', connection: {driver: 'pg', user: 'postgres', host: 'postgres'}}) 3 | c.raw('CREATE DATABASE registry_relational') 4 | .then(function (o) { 5 | process.exit(0) 6 | }) 7 | .catch(function (e) { 8 | if (e.code === '42P04') process.exit(0) 9 | else process.exit(1) 10 | }) 11 | -------------------------------------------------------------------------------- /roles/rr-follower/files/config-development.json: -------------------------------------------------------------------------------- 1 | { 2 | "connection": { 3 | "driver": "pg", 4 | "user": "postgres", 5 | "host": "postgres", 6 | "database": "registry_relational" 7 | }, 8 | "pool": { 9 | "min": 0, 10 | "max": 7 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /roles/rr-follower/files/start.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | /usr/sbin/crond 3 | cd /etc/npme/node_modules/@npm/registry-relational-models/ 4 | 5 | node bootstrap.js 6 | ret=$? 7 | if [ $ret -ne 0 ]; then 8 | exit 1 9 | fi 10 | 11 | curl --fail -XGET http://couchdbprimary:5984/registry 12 | ret=$? 13 | if [ $ret -ne 0 ]; then 14 | exit 1 15 | fi 16 | 17 | npm run create-dev 18 | cd /etc/npme/node_modules/@npm/relational-registry-follower/ 19 | ENVIRONMENT=development node ./bin/follow.js --sequence=/etc/npme/data/rr-sequence --db=http://couchdbprimary:5984/registry 20 | -------------------------------------------------------------------------------- /roles/rr-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM bcoe/rr-service:1.0.0 2 | 3 | COPY files/config-development.json /etc/npme/node_modules/@npm/registry-relational-service/config-development.json 4 | -------------------------------------------------------------------------------- /roles/rr-service/files/config-development.json: -------------------------------------------------------------------------------- 1 | { 2 | "connection": { 3 | "driver": "pg", 4 | "user": "postgres", 5 | "host": "postgres", 6 | "database": "registry_relational" 7 | }, 8 | "pool": { 9 | "min": 0, 10 | "max": 7 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /roles/tools/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:4.2.2 2 | 3 | RUN mkdir -p /usr/workdir 4 | WORKDIR /usr/workdir 5 | 6 | COPY files /usr/workdir 7 | 8 | RUN npm install 9 | 10 | ENTRYPOINT ["npm", "run"] 11 | -------------------------------------------------------------------------------- /roles/tools/files/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-1"] 3 | } 4 | -------------------------------------------------------------------------------- /roles/tools/files/config/replication.config.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | target: 'couchdbsecondary:5984', 4 | upstream: 'couchdbprimary:5984', 5 | databases: ['registry'], 6 | continuous: true 7 | } 8 | ]; 9 | -------------------------------------------------------------------------------- /roles/tools/files/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npmo-docker-tools", 3 | "description": "A docker setup for npmo", 4 | "main": "index.js", 5 | "scripts": { 6 | "replication": "babel-node -- tools/couchdb/replication.js config/replication.config.js", 7 | "test:nginx": "curl http://nginx:8000/blerg" 8 | }, 9 | "license": "ISC", 10 | "devDependencies": { 11 | "babel": "^6.0.15", 12 | "babel-cli": "^6.1.2", 13 | "babel-preset-es2015": "^6.1.2", 14 | "babel-preset-stage-1": "^6.1.2", 15 | "node-fetch": "^1.3.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /roles/tools/files/tools/couchdb/couchdb.js: -------------------------------------------------------------------------------- 1 | import fetch from 'node-fetch'; 2 | 3 | export default class CouchDB { 4 | static defaultHeaders = { 5 | 'Content-Type': 'application/json' 6 | }; 7 | 8 | /** 9 | * host: string host, eg 'your-database:5984' 10 | */ 11 | constructor(host) { 12 | this.host = host; 13 | } 14 | 15 | /** 16 | * Set up replication between this CouchDB instance and an upstream. 17 | * 18 | * upstream: string host of the upstream couch, eg 'another-database:5984' 19 | * database: string database to replicate, eg 'data' 20 | * opts: options object 21 | * continuous: boolean should the replication be continuous. Note: continuous replication is 22 | * 'forgotten' when the databse machiner restarts. 23 | * 24 | * Returns a Promise for the result. 25 | */ 26 | replicateFrom(upstream, database, opts={}) { 27 | const { continuous = false } = opts; 28 | 29 | // source: the upstream hostname and the target database 30 | // target: the local database name 31 | // continuous: should we follow the changes? 32 | const body = { 33 | source: `http://${upstream}/${database}`, 34 | target: `${database}`, 35 | continuous 36 | }; 37 | 38 | return fetch(`http://${this.host}/_replicate`, { 39 | method: 'POST', 40 | headers: CouchDB.defaultHeaders, 41 | body: JSON.stringify(body) 42 | }) 43 | .then((res) => res.json()) 44 | .then((body) => { 45 | // TODO detect missing DB and create it? 46 | if (body.error) { 47 | throw new Error(`Failed to setup replication: ${body.error} — ${body.reason}`); 48 | } 49 | return body; 50 | }); 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /roles/tools/files/tools/couchdb/replication.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This tool sets up replication between CouchDB nodes. 3 | */ 4 | import path from 'path'; 5 | import CouchDB from './couchdb'; 6 | 7 | const replicationConfigPath = path.resolve(__dirname, '../..', process.argv[2]); 8 | const replicationConfig = require(replicationConfigPath); 9 | 10 | /** 11 | * Perform Promise operations in sequence. Takes an array of functions. 12 | */ 13 | const sequence = (fns) => { 14 | return fns.reduce( 15 | (pPrev, fn) => pPrev.then(fn), 16 | Promise.resolve() 17 | ); 18 | }; 19 | 20 | const setupReplication = ({target, upstream, continuous=false, databases=[]}) => { 21 | const db = new CouchDB(target); 22 | return sequence( 23 | databases.map((database) => () => { 24 | console.log( 25 | `${db.host}: replicating ${upstream}/${database} ${continuous ? '(continuous)' : ''}` 26 | ); 27 | return db.replicateFrom(upstream, database, { continuous }); 28 | }) 29 | ); 30 | }; 31 | 32 | sequence(replicationConfig.map((item) => () => setupReplication(item))) 33 | .then( 34 | () => console.log('Done!'), 35 | (why) => console.error(why.stack) 36 | ); 37 | --------------------------------------------------------------------------------