Mobrovac has submitted this change and it was merged. Change subject: Update to service-template-node 0.1.3 ......................................................................
Update to service-template-node 0.1.3 Changes: - send CORS and CSP headers - update dependencies - use app.loger when req.logger is not available - various small fixes and updates Please test this release, mainly due to rather restrictive CSP headers. Bug: T96126 Change-Id: Ibbe14ffec488b5791ab3ea4ff5923aee9e2c0ae1 --- M .gitignore M .jshintignore D Dockerfile M app.js M config.prod.yaml A dist/init-scripts/systemd.erb A dist/init-scripts/sysvinit.erb A dist/init-scripts/upstart.erb M doc/README.md M doc/commands.md A doc/deployment.md M doc/template.md M lib/util.js M package.json D scripts/docker.js A scripts/gen-init-scripts.rb A targets.yaml M test/features/app/app.js 18 files changed, 551 insertions(+), 205 deletions(-) Approvals: BearND: Looks good to me, approved Mobrovac: Verified; Looks good to me, approved diff --git a/.gitignore b/.gitignore index 5b6fe76..bc02473 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +Dockerfile coverage config.yaml node_modules diff --git a/.jshintignore b/.jshintignore index 1c69eee..c052634 100644 --- a/.jshintignore +++ b/.jshintignore @@ -1,3 +1,5 @@ coverage node_modules test +www +static diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index f8f2496..0000000 --- a/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM buildpack-deps:jessie - -# install node && npm -RUN apt-get update && apt-get install -y nodejs npm && rm -rf /var/lib/apt/lists/* -# link /usr/bin/node to /usr/bin/nodejs -RUN ln -s /usr/bin/nodejs /usr/bin/node - -# copy the repo files over -RUN mkdir -p /opt/service -ADD . /opt/service -# install the dependencies -WORKDIR /opt/service -RUN npm install - -# start the server -CMD ["/usr/bin/npm", "start"] - diff --git a/app.js b/app.js index 88943b1..5f9e033 100644 --- a/app.js +++ b/app.js @@ -31,6 +31,31 @@ if(!app.conf.port) { app.conf.port = 8888; } if(!app.conf.interface) { app.conf.interface = '0.0.0.0'; } if(!app.conf.compression_level) { app.conf.compression_level = 3; } + if(app.conf.cors === undefined) { app.conf.cors = '*'; } + if(!app.conf.csp) { + app.conf.csp = + "default-src 'self'; object-src 'none'; media-src *; img-src *; style-src *; frame-ancestors 'self'"; + } + + // set outgoing proxy + if(app.conf.proxy) { + process.env.HTTP_PROXY = app.conf.proxy; + } + + // set the CORS and CSP headers + app.all('*', function(req, res, next) { + if(app.conf.cors !== false) { + res.header('Access-Control-Allow-Origin', app.conf.cors); + res.header('Access-Control-Allow-Headers', 'Accept, X-Requested-With, Content-Type'); + } + res.header('X-XSS-Protection', '1; mode=block'); + res.header('X-Content-Type-Options', 'nosniff'); + res.header('X-Frame-Options', 'SAMEORIGIN'); + res.header('Content-Security-Policy', app.conf.csp); + res.header('X-Content-Security-Policy', app.conf.csp); + res.header('X-WebKit-CSP', app.conf.csp); + next(); + }); // disable the X-Powered-By header app.set('x-powered-by', false); diff --git a/config.prod.yaml b/config.prod.yaml index aad96fd..0b2e676 100644 --- a/config.prod.yaml +++ b/config.prod.yaml @@ -18,7 +18,7 @@ # Statsd metrics reporter metrics: - type: txstatsd + type: statsd host: statsd.eqiad.wmnet port: 8125 diff --git a/dist/init-scripts/systemd.erb b/dist/init-scripts/systemd.erb new file mode 100644 index 0000000..78b5be3 --- /dev/null +++ b/dist/init-scripts/systemd.erb @@ -0,0 +1,25 @@ +[Unit] +Description=<%= @description ? @service_name + ' - ' + @description : @service_name %> +Documentation=<%= @homepage %> +After=network.target local-fs.target + +[Service] +Type=simple +LimitNOFILE=<%= @no_file %> +PIDFile=%t/<%= @service_name %>.pid +User=<%= @service_name %> +Group=<%= @service_name %> +WorkingDirectory=/srv/deployment/<%= @service_name %>/deploy +Environment="NODE_PATH='/srv/deployment/<%= @service_name %>/deploy/node_modules'" "<%= @service_name.gsub(/[^a-z0-9_]/, '_').upcase %>_PORT=<%= @port %>" +ExecStart=/usr/bin/nodejs src/server.js -c /etc/<%= @service_name %>/config.yaml +Restart=always +RestartSec=5 +StandardOutput=syslog +StandardError=syslog +SyslogIdentifier=<%= @service_name %> +TimeoutStartSec=5 +TimeoutStopSec=60 + +[Install] +WantedBy=multi-user.target + diff --git a/dist/init-scripts/sysvinit.erb b/dist/init-scripts/sysvinit.erb new file mode 100644 index 0000000..f889596 --- /dev/null +++ b/dist/init-scripts/sysvinit.erb @@ -0,0 +1,172 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: <%= @service_name %> +# Required-Start: $local_fs $network $remote_fs $syslog +# Required-Stop: $local_fs $network $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: <%= @description || @service_name %> +# Description: <%= @description ? @service_name + ' - ' + @description : @service_name %> +### END INIT INFO + + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="<%= @service_name %> service" +NAME=<%= @service_name %> +SCRIPT_PATH=/srv/deployment/$NAME/deploy/src/server.js +DAEMON="/usr/bin/nodejs $SCRIPT_PATH" +DAEMON_ARGS="-c /etc/$NAME/config.yaml" +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME + +# Exit if the package is not installed +[ -e "$SCRIPT_PATH" ] || exit 0 + +# Read configuration variable file if it is present +# NOTE: only the DAEMON_ARGS var should be set in the file if present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# export some variables into the process' environment +export PORT +export INTERFACE +export NODE_PATH=/srv/deployment/$NAME/deploy/node_modules +export <%= @service_name.gsub(/[^a-z0-9_]/, '_').upcase %>_PORT=<%= @port %> + + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # up the number of fds [sockets] from 1024 + ulimit -n <%= @no_file %> + + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + + start-stop-daemon --start --quiet --pidfile $PIDFILE -bm \ + -c $NAME:$NAME --test \ + --exec /bin/sh -- \ + -c "$DAEMON $DAEMON_ARGS 2>&1 | logger -i -t $NAME" \ + || return 1 + start-stop-daemon --start --quiet --pidfile $PIDFILE -bm \ + -c <%= @service_name %>:<%= @service_name %> \ + --exec /bin/sh -- \ + -c "$DAEMON $DAEMON_ARGS 2>&1 | logger -i -t $NAME" \ + || return 2 + echo "Started <%= @service_name %> service on port <%= @port %>" + + + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. + sleep 5 +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/60/KILL/5 --pidfile $PIDFILE --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/5/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + # + # If the daemon can reload its configuration without + # restarting (for example, when it is sent a SIGHUP), + # then implement that here. + # + start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME + return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac + diff --git a/dist/init-scripts/upstart.erb b/dist/init-scripts/upstart.erb new file mode 100644 index 0000000..4cffcb5 --- /dev/null +++ b/dist/init-scripts/upstart.erb @@ -0,0 +1,24 @@ +# Upstart job for <%= @service_name %> + +description "<%= @description ? @service_name + ' - ' + @description : @service_name %>" + +start on (local-filesystems and net-device-up IFACE!=lo) +stop on runlevel [!2345] + +# up ulimit -n a bit +limit nofile <%= @no_file %> <%= @no_file %> + +setuid "<%= @service_name %>" +setgid "<%= @service_name %>" + +env NODE_PATH="/srv/deployment/<%= @service_name %>/deploy/node_modules" +env <%= @service_name.gsub(/[^a-zA-Z0-9_]/, '_').upcase %>_PORT="<%= @port %>" + +respawn + +# wait 60 seconds for a graceful restart before killing the master +kill timeout 60 + +chdir /srv/deployment/<%= @service_name %>/deploy +exec /usr/bin/nodejs src/server.js -c /etc/<%= @service_name %>/config.yaml >> /var/log/<%= @service_name %>/main.log 2>&1 + diff --git a/doc/README.md b/doc/README.md index 26adc44..d837f0a 100644 --- a/doc/README.md +++ b/doc/README.md @@ -8,6 +8,7 @@ 3. [Configuration](config.md) 4. [Useful Commands](commands.md) 5. [Coding Guide](coding.md) +6. [Deployment](deployment.md) Have fun while creating RESTful API services! diff --git a/doc/commands.md b/doc/commands.md index e1bc803..01b6c5a 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -95,3 +95,7 @@ After you log out completely and log back in, you should be able to run the above scripts without resorting to `sudo`. +## Deployment + +See [this document](deployment.md) for how to get ready to deploy your service. + diff --git a/doc/deployment.md b/doc/deployment.md new file mode 100644 index 0000000..bb93f7d --- /dev/null +++ b/doc/deployment.md @@ -0,0 +1,164 @@ +# Deployment + +Getting your service ready to be deployed on WMF production machines involves +several tasks. This document explains the steps needed to get started and how to +keep your deployable copy up-to-date. + +## Repositories + +Because Node.js services use npm dependencies which can be binary, these need to +be pre-built. Therefore, two repositories are needed; one for the source code of +your service, and the other, so-called *deploy* repository. Both should be +available as WM's Gerrit repositories with the paths +*mediawiki/services/your-service-name* and +*mediawiki/services/your-service-name/deploy*. When [requesting +them](https://www.mediawiki.org/wiki/Git/New_repositories/Requests) ask for the +former to be a clone of [the service +template](https://github.com/wikimedia/service-template-node) and the latter to +be empty. + +It is important to note that the deploy repository is only to be updated +directly before (re-)deploying the service, and not on each patch merge entering +the *master* branch of the regular repository. In other words, **the deploy +repository mirrors the code deployed in production at all times**. + +The remainder of the document assumes these two repositories have been created +and that you have cloned them using your Gerrit account, i.e. not anonymously, +with the following outline: + +``` +~/code/ + |- your-service + -- deploy +``` + +Furthermore, it is assumed that you have initialised the deploy repository: + +```bash +$ cd ~/code/deploy +$ git review -s +$ touch README.md +$ git add README.md +$ git commit -m "Initial commit" +$ git push -u origin master # or git review -R if this fails +# go to Gerrit and +2 your change, if needed and then: +$ git pull +``` + +Finally, if you haven't yet done so, do [basic service +configuration](config.md). + +The remainder of the document refers to these two repositories as the *source +repository* and the *deploy repository*, respectively. + +## Configuration + +The service template includes an automation script which updates the deploy +repository, but it needs to be configured properly in order to work. + +### package.json + +The first part of the configuration involves keeping your source repository's +`package.json` updated. Look for its [deploy stanza](../package.json#L49). +Depending on the exact machine on which your service will be deployed, you may +need to set `target` to either `ubuntu` or `debian`. + +The important thing is keeping the `dependencies` field up to date at all times. +There you should list all of the extra packages that are needed in order to +build the npm module dependencies. The `_all` field denotes packages which +should be installed regardless of the target distribution, but you can add +other, distribution-specific package lists, e.g.: + +```javascript +"deploy": { + "target": "ubuntu", + "dependencies": { + "ubuntu": ["pkg1", "pkg2"], + "debian": ["pkgA", "pkgB"], + "_all": ["pkgOne", "pkgTwo"] + } +} +``` + +In this example, with the current configuration, packages *pkg1*, *pkg2*, +*pkgOne* and *pkgTwo* are going to be installed before building the +dependencies. If, instead, the target is changed to `debian`, then *pkgA*, +*pkgB*, *pkgOne* and *pkgTwo* are selected. + +As a rule of thumb, **whenever you need to install extra packages into your +development environment for satisfying node module dependencies, add them to +*deploy.dependencies* to ensure the successful build and update of the deploy +repository**. + +### Local git + +The script needs to know where to find your local copy of the deploy repository. +To that end, when in your source repository, run: + +``` +git config deploy.dir /absolute/path/to/deploy/repo +``` + +Using the aforementioned local outline, you would type: + +``` +git config deploy.dir /home/YOU/code/deploy +``` + +The source repository is itself a submodule of the deploy repository. If its +name as specified in `package.json`'s `name` field does not match the actual +repository's name in Gerrit, run: + +``` +git config deploy.name name_in_gerrit +``` + +That will make the system look for the repository +`mediawiki/services/name_in_gerrit` when checking it out in the deploy +repository. + +## Testing + +Before updating the deploy repository you need to make sure your configuration +works as expected. To do that, in your source repository run: + +``` +./server.js docker-test +``` + +The script will build a new Docker image, install the needed packages and npm +dependencies and run the test suite. Tweak your code and configuration until +everything works as expected (and commit those changes). + +## Update + +The final step is updating the deploy repository. From the source repository +run: + +``` +./server.js build --deploy-repo +``` + +The script will: +- create the proper deploy repository outline +- fetch the updates +- ensure the submodule is present +- update the submodule +- build the npm dependencies +- commit the changes with a pretty-formatted message + +There is also a handy shortcut for sending the patch to Gerrit immediately. To +do so, add the `--review` argument to the call: + +``` +./server.js build --deploy-repo --review +``` + +Note that if no changes were made to the source repository, the script aborts +its execution. If, nevertheless, you need to rebuild the dependencies, you can +do so using: + +``` +./server.js build --deploy-repo --force +``` + diff --git a/doc/template.md b/doc/template.md index b8c9af9..f9d28ad 100644 --- a/doc/template.md +++ b/doc/template.md @@ -41,7 +41,7 @@ The WMF is in the process of switching its production servers to Debian Jessie. As people developing services might use different platforms, the template provides also a Dockerfile, with which one can execute their service inside a -container running Debian Jessie. +container running the production OS. ## Repository Outline @@ -62,6 +62,6 @@ client-side JS, etc.) - [`test`](../test/) - contains the test files for the example routes in the template; you should add your own here -- [`scripts/docker.js`](../scripts/docker.js) - a utility script building the - service's docker image and starting the container +- docker script - a utility script building the service's docker image and + starting the container, now part of [service-runner](wikimedia/service-runner) diff --git a/lib/util.js b/lib/util.js index 58ed1e3..9db1055 100644 --- a/lib/util.js +++ b/lib/util.js @@ -190,8 +190,8 @@ level = 'info'; } // log the error - req.logger.log(level + - (errObj.component ? '/' + errObj.component : '/' + errObj.status), + (req.logger || app.logger).log(level + '/' + + (errObj.component ? errObj.component : errObj.status), errForLog(errObj)); // let through only non-sensitive info var respBody = { diff --git a/package.json b/package.json index 4223f67..3c69df8 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,8 @@ "scripts": { "start": "service-runner", "test": "mocha", - "docker-start": "./scripts/docker.js", - "docker-test": "./scripts/docker.js --test", - "docker-cover": "./scripts/docker.js --cover", + "docker-start": "service-runner docker-start", + "docker-test": "service-runner docker-test", "coverage": "istanbul cover _mocha -- -R spec" }, "repository": { @@ -27,26 +26,32 @@ "contributors": [], "license": "Apache2", "bugs": { - "url": "https://phabricator.wikimedia.org/tag/services/" + "url": "https://phabricator.wikimedia.org/tag/service-template-node/" }, "homepage": "https://github.com/wikimedia/service-mobileapp-node", "dependencies": { - "bluebird": "^2.9.14", - "body-parser": "^1.12.1", - "bunyan": "^1.3.4", + "bluebird": "^2.9.24", + "body-parser": "^1.12.3", + "bunyan": "^1.3.5", "compression": "^1.4.3", "domino": "^1.0.18", - "express": "^4.12.2", + "express": "^4.12.3", "js-yaml": "^3.2.7", "node-uuid": "^1.4.3", - "preq": "^0.3.12", - "service-runner": "^0.1.5" + "preq": "^0.3.13", + "service-runner": "^0.1.8" }, "devDependencies": { "assert": "^1.3.0", - "istanbul": "^0.3.8", - "mocha": "^2.2.1", + "istanbul": "^0.3.13", + "mocha": "^2.2.4", "mocha-jshint": "0.0.9", "mocha-lcov-reporter": "0.0.1" + }, + "deploy": { + "target": "ubuntu", + "dependencies": { + "_all": [] + } } } diff --git a/scripts/docker.js b/scripts/docker.js deleted file mode 100755 index 0b4b433..0000000 --- a/scripts/docker.js +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env node - - -'use strict'; - -var fs = require('fs'); -var spawn = require('child_process').spawn; -var P = require('bluebird'); - - -// load info from the package definition -var pkg = require('../package.json'); -// load info from the service-runner config file -var config = require('js-yaml').safeLoad(fs.readFileSync(__dirname + '/../config.yaml')); - -// use the package's name as the image name -var img_name = pkg.name; -// the container's name -var name = pkg.name + '-' + Date.now() + '-' + Math.floor(Math.random() * 1000); - -// holds the curently running process -var child; - - -/** - * Wraps a child process spawn in a promise which resolves - * when the child process exists. - * - * @param {Array} args the command and its arguments to run (uses /usr/bin/env) - * @return {Promise} the promise which is fulfilled once the child exists - */ -function promised_spawn(args) { - - return new P(function(resolve, reject) { - child = spawn('/usr/bin/env', args, {stdio: 'inherit'}); - child.on('exit', resolve); - }); - -} - - -/** - * Spawns a docker process which (re)builds the image - * - * @return {Promise} the promise starting the build - */ -function build_img() { - - return promised_spawn(['docker', 'build', '-t', img_name, '.']); - -} - - -/** - * Starts the container either using the default script - * (npm start) or the test script (npm test) if do_tests is set - * - * @param {Object} options additional options - * @prop {Boolean} tests whether to start the tests instead of the service - * @prop {Boolean} coverage whether to start the tests and coverage instead of the service - * @return {Promise} the promise starting the container - */ -function start_container(options) { - - var cmd = ['docker', 'run', '--name', name]; - - // list all of the ports defined in the config file - config.services.forEach(function(srv) { - srv.conf = srv.conf || {}; - srv.conf.port = srv.conf.port || 8888; - cmd.push('-p', srv.conf.port + ':' + srv.conf.port); - }); - - // append the image name to create a container from - cmd.push(img_name); - - // use a different command to run inside if - // we have to run the tests or coverage - if(options.tests) { - cmd.push('/usr/bin/npm', 'test'); - } else if(options.coverage) { - cmd.push('/usr/bin/npm', 'run-script', 'coverage'); - } - - // ok, start the container - return promised_spawn(cmd); - -} - - -/** - * Deletes the container - * - * @return {Promise} the promise removing the container - */ -function remove_container() { - - return promised_spawn(['docker', 'rm', name]); - -} - - -/** - * Main process signal handler - */ -function sig_handle() { - if(child) { - child.kill('SIGINT'); - } -} - - -function main(options) { - - // trap exit signals - process.on('SIGINT', sig_handle); - process.on('SIGTERM', sig_handle); - - // change the dir - process.chdir(__dirname + '/..'); - - // start the process - return build_img() - .then(function() { - return start_container(options); - }) - .then(remove_container); - -} - - -if(module.parent === null) { - - var opts = { - tests: false, - coverage: false - }; - - // check for command-line args - var args = process.argv.slice(2); - var arg; - while((arg = args.shift()) !== undefined) { - switch(arg) { - case '-t': - case '--test': - opts.tests = true; - break; - case '-c': - case '--cover': - opts.coverage = true; - break; - default: - console.log('This is a utility script for starting service containers using docker.'); - console.log('Usage: ' + process.argv.slice(0, 2).join(' ') + ' [OPTIONS]'); - console.log('Options are:'); - console.log(' -t, --test instead of starting the service, run the tests'); - console.log(' -c, --cover run the tests and report the coverage info'); - process.exit(/^-(h|-help)/.test(arg) ? 0 : 1); - } - } - - // start the process - main(opts); - -} else { - - module.exports = main; - -} - diff --git a/scripts/gen-init-scripts.rb b/scripts/gen-init-scripts.rb new file mode 100755 index 0000000..e523b97 --- /dev/null +++ b/scripts/gen-init-scripts.rb @@ -0,0 +1,71 @@ +#!/usr/bin/env ruby + + +require 'erb' +require 'json' +require 'yaml' + + +rootdir = File.expand_path(File.join(File.dirname(__FILE__), '..')) +indir = File.join(rootdir, 'dist', 'init-scripts') +outdir = indir + + +class ScriptData + + include ERB::Util + + @@suffix = {'systemd' => '.service', 'upstart' => '.conf'} + + def initialize input_dir + @template = {} + self.init input_dir + end + + def set_info root_dir + self.read_info(root_dir).each do |key, value| + self.instance_variable_set "@#{key}".to_sym, value + end + @service_name = @name + @no_file ||= 10000 + end + + def generate output_dir + @template.each do |name, erb| + File.open(File.join(output_dir, "#{@name}#{@@suffix[name]}"), 'w') do |io| + io.write erb.result(binding()) + end + end + end + + def init input_dir + Dir.glob(File.join(input_dir, '*.erb')).each do |fname| + @template[File.basename(fname, '.erb')] = ERB.new(File.read(fname)) + end + end + + def read_info root_dir + data = YAML.load(File.read(File.join(root_dir, 'config.yaml')))['services'][0]['conf'] + return data.merge(JSON.load(File.read(File.join(root_dir, 'package.json')))) + end + +end + + +if ARGV.size > 0 and ['-h', '--help'].include? ARGV[0] + puts 'This is a simple script to generate various service init scripts' + puts 'Usage: gen-init-scripts.rb [output_dir]' + exit 1 +elsif ARGV.size > 0 + outdir = ARGV[0] +end + +unless File.directory? outdir + STDERR.puts 'The output directory must exist! Aborting...' + exit 2 +end + +data = ScriptData.new indir +data.set_info rootdir +data.generate outdir + diff --git a/targets.yaml b/targets.yaml new file mode 100644 index 0000000..6a16a18 --- /dev/null +++ b/targets.yaml @@ -0,0 +1,3 @@ +debian: 'debian:jessie' +ubuntu: 'ubuntu:14.04' + diff --git a/test/features/app/app.js b/test/features/app/app.js index 0ed4b7d..ce20eda 100644 --- a/test/features/app/app.js +++ b/test/features/app/app.js @@ -25,5 +25,41 @@ }); }); + it('should set CORS headers', function() { + return preq.get({ + uri: server.config.uri + 'robots.txt' + }).then(function(res) { + assert.deepEqual(res.status, 200); + assert.deepEqual(res.headers['access-control-allow-origin'], '*'); + assert.notDeepEqual(res.headers['access-control-allow-headers'], undefined); + }); + }); + + it('should set CSP headers', function() { + return preq.get({ + uri: server.config.uri + 'robots.txt' + }).then(function(res) { + assert.deepEqual(res.status, 200); + assert.deepEqual(res.headers['x-xss-protection'], '1; mode=block'); + assert.deepEqual(res.headers['x-content-type-options'], 'nosniff'); + assert.deepEqual(res.headers['x-frame-options'], 'SAMEORIGIN'); + assert.deepEqual(res.headers['content-security-policy'], 'default-src'); + assert.deepEqual(res.headers['x-content-security-policy'], 'default-src'); + assert.deepEqual(res.headers['x-webkit-csp'], 'default-src'); + }); + }); + + it('should get static content uncompressed', function() { + return preq.get({ + uri: server.config.uri + 'static/index.html', + headers: { + 'accept-encoding': '' + } + }).then(function(res) { + // check that the response is gzip-ed + assert.deepEqual(res.headers['content-encoding'], undefined, 'Did not expect gzipped contents!'); + }); + }); + }); -- To view, visit https://gerrit.wikimedia.org/r/205863 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Ibbe14ffec488b5791ab3ea4ff5923aee9e2c0ae1 Gerrit-PatchSet: 2 Gerrit-Project: mediawiki/services/mobileapps Gerrit-Branch: master Gerrit-Owner: Mobrovac <mobro...@wikimedia.org> Gerrit-Reviewer: BearND <bsitzm...@wikimedia.org> Gerrit-Reviewer: Mobrovac <mobro...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits