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

Reply via email to