diff --git a/.travis.yml b/.travis.yml index 41ba3d2..39bf0b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,33 +1,13 @@ +sudo: required language: python python: - "2.7" -addons: - postgresql: "9.3" +services: + - docker -install: - - sudo apt-get update -y && - sudo apt-get install -y - build-essential htop - python-dev python-pip python-virtualenv - libxml2-dev libxslt1-dev - libcurl4-openssl-dev libssl-dev zlib1g-dev libpcre3-dev - libldap2-dev libsasl2-dev - libjpeg-dev - libfreetype6-dev - libpq-dev - npm - memcached - - npm install - - pip install -r requirements.txt +before_install: + - docker build -t fum . + - docker build -t fum-test docker/test/ - - mkdir -p media/portraits/full media/portraits/thumb media/portraits/badge - - psql -c 'create database fum;' -U postgres - - cp local_settings.py.template local_settings.py - - sed -i local_settings.py - -e "s/^SECRET_KEY =.*$/SECRET_KEY = 'test'/" - -e "s/company/futurice/g" - -e "s/Company/Futurice/g" - -e 's/example\.com/futurice.com/g' - -script: python manage.py test --settings=fum.settings.test --noinput fum +script: docker run fum-test diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9c50a9e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,51 @@ +FROM ubuntu:16.04 +MAINTAINER Topi Paavilainen + +### APT-GET ### + +RUN apt-get update +RUN apt-get install -y \ + build-essential htop \ + python-dev python-pip \ + libxml2-dev libxslt1-dev \ + libcurl4-openssl-dev libssl-dev zlib1g-dev libpcre3-dev \ + libldap2-dev libsasl2-dev \ + libjpeg-dev \ + libfreetype6-dev \ + libpq-dev \ + npm \ + memcached \ + openjdk-8-jdk \ + nginx \ + supervisor + +RUN ln -s /usr/bin/nodejs /usr/bin/node + +RUN apt-get install -y wget unzip +RUN wget -q http://archive.apache.org/dist/lucene/solr/3.6.2/apache-solr-3.6.2.zip +RUN unzip apache-solr-3.6.2.zip + +WORKDIR /opt/app + +### DEPENDENCIES ### +COPY package.json /opt/ +RUN cd /opt/ && npm install +RUN ln -s /opt/node_modules/less/bin/lessc /usr/bin/ + +COPY requirements.txt /opt/app/ +RUN apt-get install -y libffi-dev && pip install -r requirements.txt + +COPY . /opt/app/ + +RUN mkdir -p /opt/media/portraits/full /opt/media/portraits/thumb /opt/media/portraits/badge +RUN mkdir /opt/static + +USER root +ENV DJANGO_SETTINGS_MODULE fum.settings.base +ENV SECRET_KEY default_insecure_secret + +EXPOSE 8000 +COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf +ADD docker/nginx.conf /etc/nginx/nginx.conf + +CMD ["bash", "docker/start.sh"] diff --git a/README.md b/README.md index 362c504..1ad0bd6 100644 --- a/README.md +++ b/README.md @@ -21,18 +21,35 @@ sed -e "s/^SECRET_KEY =.*$/SECRET_KEY = 'test'/" \ # set USE_TLS=False if some LDAP connections don't work # Change IT_TEAM to an existing group you're part of to get SUDO permission ``` +Run using Docker +================ +First build & run your local 389 ldap-server on docker. Insert your 389-servers IP address to `local_settings.py > LDAP_CONNECTION > uri`. 389-server should be running while building FUM. +Set `USE_TLS` and `CHANGES_SOCKET_ENABLED` in `settings/base.py` to False. -Run using Vagrant -================= -Enter your LDAP username in `vagrant/REMOTE_USER`, e.g.: -```bash -echo username >vagrant/REMOTE_USER +Build the docker image: +``` +docker build -t fum-docker futurice-ldap-user-manager/ ``` -```bash -vagrant up +Run: +``` +docker run -p 8080:8000 fum-docker +``` + +Now FUM should be running locally and can be viewed on `localhost:8080` + +To search with Solr+Haystack: +```` +docker exec ./manage.py update_index +```` + +Running tests: ``` -[localhost:8000](http://localhost:8000) +docker exec ./manage.py test --settings=fum.settings.test_live +``` +FUM should be running while running tests. + + Develop locally using Procboy ============================= diff --git a/Vagrantfile b/Vagrantfile deleted file mode 100644 index ab5991c..0000000 --- a/Vagrantfile +++ /dev/null @@ -1,21 +0,0 @@ -# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! -VAGRANTFILE_API_VERSION = "2" - -Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| - # Every Vagrant virtual environment requires a box to build off of. - config.vm.box = "ubuntu/trusty64" - - config.vm.provider "virtualbox" do |v| - v.memory = 1024 - end - - config.vm.network "forwarded_port", guest: 8000, host: 8000 - - config.vm.provision "shell", path: "vagrant/provision-root.sh" - config.vm.provision "shell", privileged: false, - path: "vagrant/provision-user.sh" - - config.vm.provision "shell", run: "always", path: "vagrant/always-root.sh" - config.vm.provision "shell", run: "always", privileged: false, - path: "vagrant/always-user.sh" -end diff --git a/assetgen.yaml b/assetgen.yaml index d38aafd..d0d5597 100644 --- a/assetgen.yaml +++ b/assetgen.yaml @@ -59,7 +59,7 @@ generate: - fum/common/static/css/main.less - fum/common/static/css/imgareaselect-animated.css -output.directory: static +output.directory: /opt/static output.hashed: true output.manifest: assets.json diff --git a/docker/dev_nginx.conf b/docker/dev_nginx.conf new file mode 100644 index 0000000..5f66461 --- /dev/null +++ b/docker/dev_nginx.conf @@ -0,0 +1,44 @@ +user www-data; +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + sendfile off; + keepalive_timeout 65; + client_max_body_size 100M; + server { + server_name localhost docker.dev; + listen 8000; + client_max_body_size 100M; + access_log off; + sendfile off; + expires 0; + + location / { + client_max_body_size 100M; + + proxy_pass http://127.0.0.1:8001; + proxy_set_header Host $host:8000; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-Port 8000; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_redirect off; + } + } +} +daemon off; diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 0000000..eea2b62 --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,48 @@ +user www-data; +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + sendfile off; + keepalive_timeout 65; + client_max_body_size 100M; + server { + server_name localhost; + listen 8000; + client_max_body_size 100M; + access_log off; + sendfile on; + expires 0; + + location /static { + alias /opt/static; + } + + location / { + client_max_body_size 100M; + + uwsgi_pass 127.0.0.1:3031; + + include uwsgi_params; + + uwsgi_param Host $host; + uwsgi_param X-Forwarded-Host $server_name; + uwsgi_param X-Real-IP $remote_addr; + } + } +} +daemon off; diff --git a/docker/start.sh b/docker/start.sh new file mode 100644 index 0000000..ad75428 --- /dev/null +++ b/docker/start.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +./manage.py build_solr_schema > /tmp/schema.xml +cp /tmp/schema.xml /apache-solr-3.6.2/example/solr/conf/ +cp /apache-solr-3.6.2/example/solr/conf/stopwords.txt /apache-solr-3.6.2/example/solr/conf/stopwords_en.txt +export PATH=$PATH:/apache-solr-3.6.2/example/solr/conf/ + +assetgen --profile dev assetgen.yaml --force +./manage.py collectstatic --noinput +./manage.py migrate --noinput + +# development: be logged in as specified REMOTE_USER +replaceinfile() { + find $1 -type f -exec sed -i "s~$2~$3~g" {} \; +} +if [[ -n "$REMOTE_USER" ]]; then + replaceinfile "/etc/supervisor/conf.d/supervisord.conf" "#devuser" "environment=REMOTE_USER=\"$REMOTE_USER\"" +fi +# DEBUG +if [[ "$DEBUG" == "true" ]]; then + FUM_CMD="command=./manage.py runserver --nostatic 8001" + cp docker/dev_nginx.conf /etc/nginx/nginx.conf +else + FUM_CMD="command=/usr/local/bin/uwsgi -s 127.0.0.1:3031 --wsgi-file uwsgi.py" +fi +replaceinfile "/etc/supervisor/conf.d/supervisord.conf" "#fumcmd" "$FUM_CMD" + +/usr/bin/supervisord -c /etc/supervisor/supervisord.conf diff --git a/docker/supervisord.conf b/docker/supervisord.conf new file mode 100644 index 0000000..f685381 --- /dev/null +++ b/docker/supervisord.conf @@ -0,0 +1,16 @@ +[supervisord] +nodaemon=true + +[program:fum] +#fumcmd +directory=/opt/app/ +startsecs=5 +#devuser + +[program:nginx] +command=/usr/sbin/nginx -c /etc/nginx/nginx.conf + +[program:solr] +command=java -jar start.jar +directory=/apache-solr-3.6.2/example/ +environment=PATH=$PATH:/apache-solr-3.6.2/example/solr/conf/ diff --git a/docker/test/Dockerfile b/docker/test/Dockerfile new file mode 100644 index 0000000..49699ac --- /dev/null +++ b/docker/test/Dockerfile @@ -0,0 +1,10 @@ +FROM fum + +RUN cp local_settings.py.template local_settings.py +RUN sed -i local_settings.py \ + -e "s/^SECRET_KEY =.*$/SECRET_KEY = 'test'/" \ + -e "s/company/futurice/g" \ + -e "s/Company/Futurice/g" \ + -e 's/example\.com/futurice.com/g' + +CMD python manage.py test --settings=fum.settings.test --noinput fum diff --git a/fabfile.py b/fabfile.py deleted file mode 100644 index 4fc83d3..0000000 --- a/fabfile.py +++ /dev/null @@ -1,315 +0,0 @@ -from contextlib import contextmanager as _contextmanager -from fabric.api import cd, env, local, run, sudo, task, put, hide -from fabric.context_managers import prefix, settings -from fabric.contrib.files import append -from fabric.operations import prompt -import os, re, tempfile, time -import subprocess, json - -env.local_project_root = os.path.dirname(__file__) -env.local_python_packages_dir = os.path.join(env.local_project_root, 'dist') -# SETTINGS -env.project = 'fum' -env.deploy_group = 'Users' # deploy users must belong to this group -env.www_root = '/srv/www' -env.owner = 'www-data' -env.release = '%s_%s' % (env.project, time.strftime('%Y%m%d%H%M%S')) -env.pkg = '/tmp/{release}.tar.gz'.format(**env) -env.apache_restart_command = '/etc/init.d/apache2 restart' -env.basepath = '{www_root}/{project}'.format(**env) -env.project_root = '{www_root}/{project}/www'.format(**env) -env.python_packages_dir = '{www_root}/{project}/dist'.format(**env) -env.virtualenv = '{www_root}/{project}/venv'.format(**env) -env.supervisor_conf_dir = '/etc/supervisor' -env.branch = 'master' - -@task -def production(): - # api.fum is an apache2 configuration - env.hosts = ['fum.futurice.com'] - env.use_sudo = True - -production() # default - -# -# -# TASKS -# -# - -#supervisor_conf() -#supervisor_restart() - -@task -def deploy(): - ask_sudo_password() - - tar_from_git() - upload_tar() - - prepare_python_packages() - synchronize_python_packages() - install_python_packages() - symlink_release() - - # anything that depends on requirements under here - prepare_hidden_files() - prepare_assets() - - manage('migrate --noinput') - - ownership() - - restart_apache() - -@task -def ownership(): - sudo('chown -R {owner} {basepath}/media {basepath}/logs'.format(**env)) - -@task -def dirs(): - sudo('mkdir -p {basepath} {basepath}/packages {basepath}/releases {basepath}/media {basepath}/dist {basepath}/logs'.format(**env)) - -@task -def setup(): - ask_sudo_password() - - dirs() - ownership() - tar_from_git() - upload_tar() - - prepare_python_packages() - synchronize_python_packages() - create_virtualenv() - install_python_packages() - symlink_release() - - prepare_hidden_files() - prepare_assets() - - restart_apache() - -@task -def prepare_hidden_files(): - sudo('cp /root/fum/local_settings.py {project_root}/'.format(**env)) - -@task -def check(): - """ check project is setup correctly - DJANGO_SETTINGS_MODULE exported in venv/bin/activate - """ - result = sudoin('env|grep DJANGO_SETTINGS_MODULE') - assert 'fum.settings.prod' in result - -@task -def reset_and_sync(): - manage('reset_db --router=default --noinput') - manage('datamigrate') - -# -# -# UNDER THE HOOD -# -# - -@task -def prepare_assets(): - with virtualenv(): - with cd(env.project_root): - sudo('assetgen --profile dev assetgen.yaml --force') - manage('collectstatic --noinput') - -@task -def restart_apache(): - cmd = getattr(env, 'apache_restart_command', '/etc/init.d/apache2 restart') - sudo(cmd, pty=False) - -def filenameToRequirement(filename): - """Converts 'package-name-1.2.3.tar.gz' to 'package-name==1.2.3'""" - match = re.match(r'(.+)-(\d.+?)(\.tar\.gz|\.tar\.bz2|\.zip)$', filename) - if not match: - return None - package, version, _extension = match.groups() - return '{package}=={version}'.format(package=package, version=version) - -@task -def prepare_python_packages(): - local('mkdir -p {local_python_packages_dir}'.format(**env)) - local('cp' - ' {local_project_root}/requirements.txt' - ' {local_python_packages_dir}/' - .format(**env)) - existing_files = set( - filenameToRequirement(filename) - for filename in os.listdir(env.local_python_packages_dir)) - missing_requirements = tempfile.NamedTemporaryFile() - for raw_line in open(os.path.join(env.local_project_root, 'requirements.txt')): - line = raw_line.strip() - if not line or line.startswith('#') or line not in existing_files: - missing_requirements.write(raw_line) - missing_requirements.flush() - local('pip install' - ' --no-use-wheel' - ' -d {env.local_python_packages_dir}' - ' --exists-action=i' - ' -r {missing_requirements_file}' - .format(env=env, missing_requirements_file=missing_requirements.name)) - missing_requirements.close() - -def rsync_up(args, source, target): - sudo('mkdir -p {target}'.format(env=env, target=target)) - sudo('chgrp {env.deploy_group} {target} && chmod -R g+w {target}'.format(env=env, target=target)) - if env.key_filename: - args += " -e 'ssh -i %s'" % env.key_filename[0] - local('rsync -r {args} {source}/ {env.user}@{env.host}:{target}'.format(env=env, args=args, source=source, target=target)) - - -@task -def synchronize_python_packages(): - rsync_up('-vP' - ' --delete' - ' --delete-excluded' - ' --include=*.tgz' - ' --include=*.tar.gz' - ' --include=*.zip' - ' --include=requirements.txt' - ' --exclude=*', - source=env.local_python_packages_dir, - target=env.python_packages_dir) - -@task -def create_virtualenv(): - sudo('pip install virtualenv --upgrade') - if hasattr(env, 'virtualenv'): - sudo('rm -rf {virtualenv} && ' - 'virtualenv' - ' --extra-search-dir={python_packages_dir}' - ' --prompt="({project})"' - ' {virtualenv}' - .format(**env)) - -@_contextmanager -def virtualenv(): - if hasattr(env, 'virtualenv'): - with prefix('source {virtualenv}/bin/activate'.format(**env)): - yield - else: - yield - -@task -def install_python_packages(): - with virtualenv(): - sudo("cat {python_packages_dir}/requirements.txt|grep -v '^--' > {python_packages_dir}/files.txt".format(**env)) - pip_install('-r <(grep -vxFf' - ' <(pip freeze)' - ' {python_packages_dir}/files.txt)' - .format(**env)) - -@task -def ask_sudo_password(): - sudo('echo Enter sudo password for {env.host}'.format(env=env)) - -def umask(value='002'): - return prefix('umask {value}'.format(value=value)) - -def pip_install(packages): - sudo('HOME={env.project_root} pip install' - ' --no-use-wheel' - ' --default-timeout=5' - ' --no-index' - ' -f file://{env.python_packages_dir}' - ' {packages}' - .format(env=env, packages=packages)) - - -@task -def manage(args): - with virtualenv(): - with cd(env.project_root): - return sudo('python manage.py {args}'.format(args=args)) - -@task -def sudoin(cmd): - with virtualenv(): - with cd(env.project_root): - return sudo(cmd) - -def aptgetall(): - all = """ - apt-get install --ignore-missing \ - build-essential curl \ - git git-core supervisor \ - libxml2-dev libxslt1-dev \ - libcurl4-openssl-dev libssl-dev zlib1g-dev checkinstall libpcre3-dev \ - libsqlite3-dev libbz2-dev \ - nodejs npm - """ - - -# -# ALIASES -# -# - -def mput(a, b, **kwargs): - if env.use_sudo: - kwargs['use_sudo'] = True - return put(a.format(**env), b.format(**env), **kwargs) - -def mrun(a, **kwargs): - if env.use_sudo: - return sudo(a.format(**env), **kwargs) - else: - return run(a.format(**env), **kwargs) - -def mlocal(a, **kwargs): - return local(a.format(**env), **kwargs) - -# -# FILE HANDLING -# -# - -def upload_tar(): - mput(env.pkg, '{basepath}/packages/') - with cd(env.basepath): - mrun('mkdir -p releases/{release}') - mrun('tar zxf packages/{release}.tar.gz -C releases/{release}') - mlocal('rm {pkg}') - -def tar_from_git(): - local('git archive --format=tar {branch} | gzip > {pkg}'.format(**env)) - -def symlink_release(): - with cd(env.basepath): - mrun('ln -s releases/{release} www.new; mv -T www.new www') - -# -# SUPERVISOR (server-wide setup with individual project settings) -# -# - -def supervisor_conf(): - mput('fum.conf', '{supervisor_conf_dir}/conf.d/') - -def start_app(): - with settings(hide('warnings',), warn_only=True): - res = mrun("supervisorctl start all") - if not res.succeeded or 'no such file' in res: - run("supervisord") - -def stop_app(): - with settings(hide('warnings',), warn_only=True): - run("supervisorctl stop all") - run("supervisorctl shutdown") - -def supervisor_restart(): - with settings(hide('warnings',), warn_only=True): - stop_app() - start_app() - -def supervisor_soft_restart_app(): - with settings(hide('warnings',), warn_only=True): - run("supervisorctl restart all") - diff --git a/fum/api/serializers.py b/fum/api/serializers.py index 3b7f97a..d13f4b8 100644 --- a/fum/api/serializers.py +++ b/fum/api/serializers.py @@ -6,7 +6,7 @@ from rest_framework.compat import smart_text from fum.models import ( - Users, Groups, Servers, Projects, EMails, Resource, EMailAliases, SSHKey, + Users, Groups, Servers, Projects, EMails, Resource, SSHKey, get_generic_email, ) from fum.common.middleware import get_current_request @@ -196,17 +196,7 @@ class EMailsSerializer(serializers.ModelSerializer): class Meta: model = EMails - fields = ('id', 'address',) - -# -# Aliases -# -class AliasesSerializer(serializers.ModelSerializer): - parent = serializers.Field(source='parent') - - class Meta: - model = EMailAliases - fields = ('id', 'address', 'parent') + fields = ('id', 'address', 'alias') # # SSH Keys diff --git a/fum/api/tests.py b/fum/api/tests.py index 80aecf5..fc17daa 100644 --- a/fum/api/tests.py +++ b/fum/api/tests.py @@ -14,6 +14,7 @@ from django.core.exceptions import ValidationError from django.db import models, transaction from django.db.models.signals import pre_delete, post_delete, post_init, post_save, m2m_changed +from django.db import IntegrityError from mock import patch, MagicMock, PropertyMock from mockldap import MockLdap @@ -23,12 +24,11 @@ from fum.ldap_helpers import test_user_ldap, ldap_cls, PoolLDAPBridge, LDAPBridge from fum.models import ( - Users, Servers, Groups, Projects, EMails, EMailAliases, BaseGroup, + Users, Servers, Groups, Projects, EMails, BaseGroup, Resource, SSHKey, ) import datetime, json, sys, copy, os, time -from pprint import pprint as pp from fum.api.changes import changes_save, rest_reverse from fum.common.ldap_test_suite import LdapSuite, LdapTransactionSuite, random_ldap_password @@ -350,6 +350,8 @@ def tearDown(self): self.mockldap.stop() def test_signals_mocked(self): + if 'test_live' in os.environ.get('DJANGO_SETTINGS_MODULE'): + return # ReconnectingLDAPBridge re-uses initial connection option_count = len(settings.LDAP_CONNECTION_OPTIONS) tls = ['initialize'] + ['set_option']*option_count + ['initialize'] @@ -673,10 +675,12 @@ def test_email_integrity(self): self.assertEqual(project.get_email().address, group_mail) self.assertEqual(self.ldap_val('mail', project), [group_mail]) - response = self.client.post("/api/projects/%s/"%project.name, - {"email": mail,}, - HTTP_X_HTTP_METHOD_OVERRIDE='PATCH',) - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + with self.assertRaises(IntegrityError): + response = self.client.post("/api/projects/%s/"%project.name, + {"email": mail,}, + HTTP_X_HTTP_METHOD_OVERRIDE='PATCH',) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(project.get_email().address, group_mail) self.assertEqual(self.ldap_val('mail', project), [group_mail]) @@ -751,7 +755,8 @@ def test_user(self): response = self.c.delete("/api/users/%s/"%name) self.assertEquals(response.status_code, 204) with self.assertRaises(KeyError): - self.ldap_val('mail', user) + self.ldap_val('mail', user) # TODO: returns [] for empty ldap entry + self.ldap_val('cn', user) # KeyError response = self.c.get("/api/users/", {}) self.assertNotContains(response, name) diff --git a/fum/api/urls.py b/fum/api/urls.py index 99bd9b6..f090370 100644 --- a/fum/api/urls.py +++ b/fum/api/urls.py @@ -10,13 +10,12 @@ router.register(r'servers', views.ServersViewSet) router.register(r'projects', views.ProjectsViewSet) router.register(r'emails', views.EMailsViewSet) -router.register(r'aliases', views.EMailAliasesViewSet) router.register(r'sshkeys', views.SSHKeysViewSet) # rest-framework serializers are not done yet; refactor mod_* code to get routings done by the framework router_urls = router.get_urls() related_urls = [] -patch_support_for_relations = ['users','resources','aliases','sudoers','projects','groups'] +patch_support_for_relations = ['users','resources','sudoers','projects','groups'] def add_related_patterns(): for p in patch_support_for_relations: for k in router.get_urls(): diff --git a/fum/api/views.py b/fum/api/views.py index 85c3949..ff2c145 100644 --- a/fum/api/views.py +++ b/fum/api/views.py @@ -22,7 +22,7 @@ from fum.api.changes import changes_save, changes_delete from fum.ldap_helpers import test_user_ldap -from fum.models import EMailAliases, Resource, Users, SSHKey +from fum.models import Resource, Users, SSHKey from fum.common.util import SMS, random_ldap_password from fum.api.serializers import UsersSerializer @@ -227,7 +227,7 @@ def aliases(self, request, username=None, name=None): if request.method =='DELETE': for alias in items: try: - a = EMailAliases.objects.get(address=alias, parent=email).delete() + a = EMails.objects.get(address=alias, alias=True).delete() except ValidationError as e: return Response(e.messages, status=403) except KeyError: @@ -236,7 +236,7 @@ def aliases(self, request, username=None, name=None): elif request.method == 'POST': for alias in items: try: - a = EMailAliases.objects.create(address=alias, parent=email) + a = EMails.objects.create(address=alias, alias=True, content_object=obj) except ValueError, e: return Response("Please set the email before adding aliases", status=400) except Exception, e: @@ -567,11 +567,6 @@ class EMailsViewSet(BaseViewSet): serializer_class = EMailsSerializer lookup_field = 'address' -class EMailAliasesViewSet(BaseViewSet): - model = EMailAliases - serializer_class = AliasesSerializer - lookup_field = 'address' - class SSHKeysViewSet(viewsets.ReadOnlyModelViewSet): model = SSHKey serializer_class = SSHKeysSerializer diff --git a/fum/common/signals.py b/fum/common/signals.py index e1551af..c52f9d4 100644 --- a/fum/common/signals.py +++ b/fum/common/signals.py @@ -8,9 +8,8 @@ from fum.api.changes import changes_update_m2m from fum.common.middleware import get_current_request from fum.decorators import receiver_subclasses -from fum.models import LDAPModel, Groups, Servers, Projects, Users, EMailAliases, EMails, Resource +from fum.models import LDAPModel, Groups, Servers, Projects, Users, EMails, Resource, ldap_email, ldap_emailalias -from pprint import pprint as pp import ldap import copy, logging @@ -20,15 +19,56 @@ # post_save # -@receiver(post_delete, sender=Projects) -def projects_email(sender, *args, **kwargs): +def project_default_email(sender, *args, **kwargs): from fum.projects.util import name_to_email instance = kwargs['instance'] if kwargs.get('created', False): - e,_ = EMails.objects.get_or_create( - object_id=instance.pk, - content_type=ContentType.objects.get_for_model(instance), - address=name_to_email(instance.name)) + kw = dict(object_id=instance.pk, content_type=ContentType.objects.get_for_model(instance)) + if not EMails.objects.filter(**kw).exists(): + e,_ = EMails.objects.get_or_create(address=name_to_email(instance.name), **kw) + +# +# pre_save +# + +def email_unique(sender, *args, **kwargs): + # remove any existing parent + instance = kwargs['instance'] + if instance.alias: + return + kw = dict(object_id=instance.content_object.pk, + content_type=ContentType.objects.get_for_model(instance.content_object), + alias=False) + # when removing parent, its fine for aliases to remain as forwarding addresses + emails = EMails.objects.filter(**kw) + for email in emails: + try: + email.skip_ldap = True + email.delete() + except Exception as e: + print(e) + pass + +@receiver(post_save, sender=EMails) +def save_email(sender, *args, **kwargs): + instance = kwargs['instance'] + if instance.alias: + # proxyaddress = save_relation (add) + instance.content_object.ldap.save_relation(parent=instance.content_object, child=u'%s'%instance.address, field=ldap_emailalias(instance)) + else: + # email = replace_relation (add/modify) + instance.content_object.ldap.replace_relation(parent=instance.content_object, child=u'%s'%instance.address, field=ldap_email(instance)) + # create alias: username@futurice.com + if settings.EMAIL_DOMAIN in instance.address and isinstance(instance.content_object, Users): + username_email = dict(address='{0}{1}'.format(instance.content_object.username, settings.EMAIL_DOMAIN)) + if not EMails.objects.filter(**username_email).exists(): + try: + em = EMails(content_object=instance.content_object, + alias=True, + **username_email) + em.save() + except Exception as e: + pass # # post_delete @@ -39,27 +79,16 @@ def projects_email(sender, *args, **kwargs): def email_delete(sender, *args, **kwargs): instance = kwargs['instance'] if instance.content_object: + if hasattr(instance, 'skip_ldap'): + return + fn = ldap_email + if instance.alias: + fn = ldap_emailalias try: - instance.content_object.ldap.delete_relation(parent=instance.content_object, child=u'%s'%instance.address, field=instance) + instance.content_object.ldap.delete_relation(parent=instance.content_object, child=u'%s'%instance.address, field=fn(instance)) except (ldap.NO_SUCH_OBJECT, ldap.NO_SUCH_ATTRIBUTE) as e: pass -@receiver(post_delete, sender=EMailAliases) -def email_alias_delete(sender, *args, **kwargs): - instance = kwargs['instance'] - try: - parent = instance.parent.content_object - except (ObjectDoesNotExist), e: - # if parent is gone, aliases are also gone - log.debug(e) - return - if not parent: - return - try: - parent.ldap.delete_relation(parent=parent, child=u'%s'%instance.address, field=instance) - except (ldap.NO_SUCH_OBJECT, ldap.NO_SUCH_ATTRIBUTE) as e: - pass - # # pre_delete # @@ -216,7 +245,7 @@ def ldap_save(sender, instance, created, **kwargs): post_save.connect(changes_save, sender=Projects) post_save.connect(changes_save, sender=Users) post_save.connect(changes_save, sender=EMails) -post_save.connect(changes_save, sender=EMailAliases) post_save.connect(changes_save, sender=Resource) -post_save.connect(projects_email, sender=Projects) +post_save.connect(project_default_email, sender=Projects) +pre_save.connect(email_unique, sender=EMails) diff --git a/fum/ldap_helpers.py b/fum/ldap_helpers.py index 0171334..09ccb18 100644 --- a/fum/ldap_helpers.py +++ b/fum/ldap_helpers.py @@ -77,7 +77,7 @@ def __init__(self, parent, dn=None, **kwargs): self._connection_bound = False self.settings = AttrDict() self.settings.uri = kwargs.get('uri', settings.LDAP_CONNECTION.get('uri')) - self.settings.START_TLS = True + self.settings.START_TLS = settings.USE_TLS self.settings.CONNECTION_OPTIONS = kwargs.get('LDAP_CONNECTION_OPTIONS', None) or settings.LDAP_CONNECTION_OPTIONS self.settings.BIND_DN = kwargs.get('BIND_DN', None) or settings.LDAP_CONNECTION.get('bind_dn') self.settings.BIND_PASSWORD = kwargs.get('BIND_PASSWORD', None) or settings.LDAP_CONNECTION.get('bind_pwd') diff --git a/fum/management/commands/datamigrate.py b/fum/management/commands/datamigrate.py index cabe3d8..89ba0d2 100644 --- a/fum/management/commands/datamigrate.py +++ b/fum/management/commands/datamigrate.py @@ -106,7 +106,8 @@ def val(v, member): ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, 0) con = ldap.initialize(ldap_server) - con.start_tls_s() + if settings.USE_TLS: + con.start_tls_s() con.simple_bind_s(dn,pw) # get users from ldap @@ -144,13 +145,16 @@ def val(v, member): try: email = EMails(address=email_address,content_object=user) email.save() + except (ValidationError, IntegrityError), e: + print u, e + try: # process email aliases also if 'proxyaddress' in v: for proxy in v['proxyaddress']: if not isEmailAddressValid(proxy): print "invalid email", proxy continue - email_alias = EMailAliases(address=proxy,parent=email) + email_alias = EMails(address=proxy,alias=True,content_object=user) email_alias.save() except (ValidationError, IntegrityError), e: print u, e @@ -159,11 +163,12 @@ def val(v, member): for ssh_key_text in (v.get(SSHKey.LDAP_ATTR) or []): try: - ssh_key = SSHKey.objects.get(key=ssh_key_text) + key = ssh_key_text.strip() + ssh_key = SSHKey.objects.get(key=key) except SSHKey.DoesNotExist: - ssh_key = SSHKey(key=ssh_key_text, title='ssh key') - ssh_key.user = user - ssh_key.save() + ssh_key = SSHKey(key=key, title='ssh key') + ssh_key.user = user + ssh_key.save() print "Users done" @@ -217,17 +222,19 @@ def add_group(g): try: email.save() except (ValidationError, IntegrityError), e: - log.debug("DUPLICATE EMAIL; ADDING RANDOM PREFIX (these are not use yet --Vesa) ::cross fingers:: "+email_address) - email.address = email_address.replace('@','-tre@') - email.save() + if not 'already exists' in unicode(e): + print e + log.debug("DUPLICATE EMAIL"+email_address) + email.save() # email aliases if 'proxyaddress' in v: for proxy in v['proxyaddress']: if not isEmailAddressValid(proxy): #print "invalid email", proxy continue - if not EMailAliases.objects.filter(address=proxy).count() and not EMails.objects.filter(address=proxy).exists(): - EMailAliases(address=proxy, parent=email).save() + if not EMails.objects.filter(address=proxy).exists(): + log.debug("parent: %s"%(email)) + EMails(address=proxy, alias=True, content_object=group).save() elif 'proxyaddress' in v: log.debug("group "+group.name+" doesn't have a primary email, but shill has email aliases... not good!") # group members diff --git a/fum/migrations/0008_auto_20170815_1134.py b/fum/migrations/0008_auto_20170815_1134.py new file mode 100644 index 0000000..71e2bf5 --- /dev/null +++ b/fum/migrations/0008_auto_20170815_1134.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('fum', '0007_remove_users_planmill_apikey'), + ] + + operations = [ + migrations.RemoveField( + model_name='emailaliases', + name='parent', + ), + migrations.DeleteModel( + name='EMailAliases', + ), + migrations.AddField( + model_name='emails', + name='alias', + field=models.BooleanField(default=False, db_index=True), + preserve_default=True, + ), + migrations.AlterUniqueTogether( + name='emails', + unique_together=set([]), + ), + ] diff --git a/fum/models.py b/fum/models.py index d4f9ce0..9547c72 100755 --- a/fum/models.py +++ b/fum/models.py @@ -39,8 +39,6 @@ from rest_framework.authtoken.models import Token -from pprint import pprint as pp - log = logging.getLogger(__name__) WEEKDAY_FRIDAY = 4 @@ -58,7 +56,7 @@ def calculate_password_valid_days(): def get_generic_email(email): if not isinstance(email, EMails): - email = email.all() + email = email.filter(alias=False) if email: return email[0] else: @@ -731,16 +729,26 @@ def clean(self): def get_absolute_url(self): return reverse('projects_detail', kwargs={'slug':self.name}) +def ldap_email(i): + i = copy.copy(i) + i.ldap_field = 'mail' + return i + +def ldap_emailalias(i): + i = copy.copy(i) + i.ldap_field = 'proxyaddress' + return i + class EMails(Mother): """ REMINDER: NOT AN LDAP MODEL """ address = models.EmailField(max_length=254, blank=True, unique=True) + alias = models.BooleanField(default=False, blank=True, db_index=True) content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField(null=True) content_object = generic.GenericForeignKey('content_type', 'object_id') restricted_fields = ['address'] - ldap_field = 'mail' @property def name(self): @@ -752,7 +760,9 @@ def get_by_name(): @property def aliases(self): - return EMailAliases.objects.filter(parent=self) + return EMails.objects.filter(alias=True, + content_type=self.content_type, + object_id=self.object_id) def isEmailAddressValid(self, email): try: @@ -764,93 +774,28 @@ def isEmailAddressValid(self, email): def clean(self): super(EMails, self).clean() if not self.isEmailAddressValid(self.address): - if not self.address: # allow empty; TODO: have it remove EMail + if not self.address: # allow empty pass else: raise ValidationError("Email is not valid") + + def __unicode__(self): + return u'%s'%(self.address) @transaction.atomic def save(self, *args, **kwargs): self.address = self.address.strip() - # DANGER: For GenericRelation as OneToOne-emulation to work (and EMailAliases to stay intact) - # - UNIQUEness ValidationError is waived - # - UPDATE first, on failure INSERT try: self.full_clean() except ValidationError, e: - if 'not valid' in unicode(e): - raise - elif 'Enter a valid' in unicode(e): - raise - elif 'already exists' in unicode(e): - pass - else: - raise - try: - existing_email = EMails.objects.filter(object_id=self.object_id, content_type=self.content_type) - res = existing_email.update(address=self.address) - if res==0: - raise Exception - existing_email = existing_email[0] - except IntegrityError, e: - raise ValidationError("Email is taken") - except Exception, e: - existing_email = None - super(EMails, self).save(*args, **kwargs) - email = existing_email or self - try: - # After super.save() no failures allowed. - self.content_object.ldap.replace_relation(parent=self.content_object, child=u'%s'%self.address, field=self) - # create identical alias for username@futurice.com - if settings.EMAIL_DOMAIN in self.address and isinstance(self.content_object, Users): - em, _ = EMailAliases.objects.get_or_create(parent=email, address='{0}{1}'.format(self.content_object.username, settings.EMAIL_DOMAIN)) - except Exception, e: - print "EMails",e - - class Meta: - unique_together = ('content_type', 'object_id') # OneToOne emulation for GenericForeignKey - -class EMailAliases(Mother): - """ REMINDER: NOT AN LDAP MODEL - - Aliases are forwarding addresses that should be unique. - For forwarding a Group email, add a User to it, not an email address. - """ - address = models.EmailField(max_length=254, unique=True) - parent = models.ForeignKey(EMails) - - restricted_fields = ['address'] - ldap_field = 'proxyaddress' - - @staticmethod - def get_by_name(): - return 'address' - - @property - def name(self): - return self.address - - def clean(self): - if EMailAliases.objects.filter(address=self.address): - raise ValidationError('Alias already in use') - - existing_email = EMails.objects.filter(address=self.address).first() - if existing_email and existing_email.address.strip() == self.address.strip(): - raise ValidationError('Alias conflicts with an existing Email address') - - super(EMailAliases, self).clean() - - @transaction.atomic - def save(self, *args, **kwargs): - # all model fields always required, manual clean here should be fine: https://code.djangoproject.com/ticket/13100 - self.full_clean() - ret = super(EMailAliases, self).save(*args, **kwargs) - try: - # After super.save() no failures allowed. - self.parent.content_object.ldap.save_relation(parent=self.parent.content_object, child=u'%s'%self.address, field=self) - except Exception, e: - pass - return ret + ok_ = ['already exists',] + if self.address: + if any(k in unicode(e) for k in ok_): + pass + else: + raise + + super(EMails, self).save(*args, **kwargs) class Resource(Mother): name = models.CharField(max_length=500) diff --git a/fum/permission.py b/fum/permission.py index 2b29228..b596496 100644 --- a/fum/permission.py +++ b/fum/permission.py @@ -61,8 +61,6 @@ def get_parent_model(self): instance_to_check = None if isinstance(self.instance, EMails): instance_to_check = self.instance.content_object - if isinstance(self.instance, EMailAliases): - instance_to_check = self.instance.parent.content_object if isinstance(self.instance, Resource): instance_to_check = self.instance.content_object return instance_to_check @@ -148,6 +146,8 @@ def can_base_check(self): if is_sudo_user: self.P.whitelist.append('SUDO enabled') self.is_in_it() + if hasattr(self.instance, 'skip_ldap') and self.instance.skip_ldap: + self.P.whitelist.append('email uniqueness delete allowed') self.editor_group_restriction() def has_user_permission_to_delete(self): @@ -191,4 +191,4 @@ def can_m2m_remove(self): ]) # CIRCULAR IMPORTS AT BOTTOM -from models import Users, Groups, EMails, EMailAliases, Resource, BaseGroup +from models import Users, Groups, EMails, Resource, BaseGroup diff --git a/fum/settings/base.py b/fum/settings/base.py index c6600bd..fe93334 100644 --- a/fum/settings/base.py +++ b/fum/settings/base.py @@ -1,5 +1,5 @@ import django.conf.global_settings as DEFAULT_SETTINGS -import os, datetime, copy +import os, datetime, copy, sys import getpass from pwd import getpwnam @@ -25,8 +25,6 @@ } OWNER = getpass.getuser() -OWNER_UID = getpwnam(OWNER).pw_uid -OWNER_GID = getpwnam(OWNER).pw_gid EMAIL_PORT = 25 EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' @@ -44,7 +42,7 @@ USE_L10N = True USE_TZ = True -MEDIA_ROOT = '{PROJECT_ROOT}/media/'.format(**locals()) +MEDIA_ROOT = '/opt/media/' MEDIA_URL = '/media/' # Location of users' portrait pictures @@ -55,9 +53,12 @@ PORTRAIT_FULL_FOLDER = '%sportraits/full/' % MEDIA_ROOT PORTRAIT_FULL_URL = '%sportraits/full/' % MEDIA_URL -STATIC_ROOT = '{PROJECT_ROOT}/static/'.format(**locals()) +STATIC_ROOT = '/opt/static/' STATIC_URL = '/static/' +STATICFILES_DIRS = () +STATICFILES_FINDERS = DEFAULT_SETTINGS.STATICFILES_FINDERS + MIDDLEWARE_CLASSES = ( 'fum.common.middleware.ThreadLocals', 'django.middleware.common.CommonMiddleware', @@ -93,7 +94,7 @@ 'django_extensions', 'crispy_forms', - #'haystack', + 'haystack', 'djangohistory', 'rest_framework', 'rest_framework_docs', @@ -257,8 +258,10 @@ } DJANGO_HISTORY_TRACK = False +SECRET_KEY = os.getenv('SECRET_KEY') + try: # add any secrets here; local_settings needs to be somewhere in PYTHONPATH (eg. project-root, user-root) from local_settings import * except Exception, e: - print "No local_settings configured, ignoring..." + sys.stderr.write("No local_settings configured, ignoring...\n") diff --git a/fum/settings/prod.py b/fum/settings/prod.py index 3f90d6c..dc1eb57 100644 --- a/fum/settings/prod.py +++ b/fum/settings/prod.py @@ -34,9 +34,6 @@ PORTRAIT_THUMB_FOLDER = '%sportraits/thumb/' % MEDIA_ROOT PORTRAIT_FULL_FOLDER = '%sportraits/full/' % MEDIA_ROOT PORTRAIT_BADGE_FOLDER = os.path.join(MEDIA_ROOT, 'portraits/badge/') -from pwd import getpwnam -OWNER_UID = getpwnam(OWNER).pw_uid -OWNER_GID = getpwnam(OWNER).pw_gid if LOGGING['handlers'].has_key('logfile'): del LOGGING['handlers']['logfile'] diff --git a/fum/settings/test.py b/fum/settings/test.py index e7d2855..efd851e 100644 --- a/fum/settings/test.py +++ b/fum/settings/test.py @@ -1,4 +1,3 @@ -import django.conf.global_settings as DEFAULT_SETTINGS import sys from base import * diff --git a/fum/users/tests.py b/fum/users/tests.py index 6cf1321..02875cc 100644 --- a/fum/users/tests.py +++ b/fum/users/tests.py @@ -122,16 +122,16 @@ def test_default_values_like_google_status_saved_into_ldap(self): def test_user_email(self): self.assertEqual(self.user.email.all().count(), 0) self.user.email.add(EMails(address='pentti@futurice.com', content_object=self.user)) - self.assertEqual(self.user.email.all().count(), 1) + self.assertEqual(self.user.email.all().count(), 2) # primary + alias self.assertEqual(self.ldap_val('mail', self.user), ['pentti@futurice.com']) self.user.title = 'something else' self.user.save() - self.assertEqual(self.user.email.all().count(), 1) + self.assertEqual(self.user.email.all().count(), 2) self.assertEqual(self.ldap_val('mail', self.user), ['pentti@futurice.com']) self.user.email.add(EMails(address='pentti2@futurice.com', content_object=self.user)) - self.assertEqual(self.user.email.all().count(), 1) - self.assertEqual(self.user.email.all()[0].address, 'pentti2@futurice.com') + self.assertEqual(self.user.email.all().count(), 2) + self.assertEqual(self.user.email.filter(alias=False)[0].address, 'pentti2@futurice.com') self.assertEqual(self.ldap_val('mail', self.user), ['pentti2@futurice.com']) self.user.email = [] # unsetting email in API might be problematic :knock wood-not so self.assertEqual(self.user.email.all().count(), 0) @@ -220,7 +220,6 @@ def test_leaving_planmill_not_allowed(self): def test_bad_password(self): - # TODO: ldap-environment (+mock) that fails when using bad password password='bad' u = self.save_safe(Users, kw=dict(first_name="Bad", last_name="Password", username='bpas', google_status=Users.ACTIVEPERSON), diff --git a/requirements.txt b/requirements.txt index 5afc7a5..db1470c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -30,3 +30,5 @@ mockldap-fork==0.1.6 slumber-fork==0.6.3 raven==5.3.1 procboy==0.1.4 + +uwsgi diff --git a/schema.xml b/schema.xml index 7c48334..143a3be 100644 --- a/schema.xml +++ b/schema.xml @@ -47,10 +47,16 @@ + + + + @@ -58,14 +64,34 @@ + + + + + + + + @@ -104,7 +130,6 @@ - diff --git a/uwsgi.py b/uwsgi.py new file mode 100644 index 0000000..95c8c91 --- /dev/null +++ b/uwsgi.py @@ -0,0 +1,5 @@ +import os +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "fum.settings.base") +application = get_wsgi_application() diff --git a/vagrant/.gitignore b/vagrant/.gitignore deleted file mode 100644 index 165d339..0000000 --- a/vagrant/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/REMOTE_USER diff --git a/vagrant/always-root.sh b/vagrant/always-root.sh deleted file mode 100755 index 8ce28d8..0000000 --- a/vagrant/always-root.sh +++ /dev/null @@ -1,9 +0,0 @@ -#! /usr/bin/env bash - -set -u # exit if using uninitialised variable -set -e # exit if some command in this script fails -trap "echo $0 failed because a command in the script failed" ERR - -ROOT_DIR="/vagrant" - -pip install -r "$ROOT_DIR"/requirements.txt diff --git a/vagrant/always-user.sh b/vagrant/always-user.sh deleted file mode 100755 index a51991a..0000000 --- a/vagrant/always-user.sh +++ /dev/null @@ -1,36 +0,0 @@ -#! /usr/bin/env bash - -set -u # exit if using uninitialised variable -set -e # exit if some command in this script fails -trap "echo $0 failed because a command in the script failed" ERR - - -cd /vagrant - -npm install - -mkdir -p media/portraits/full media/portraits/thumb media/portraits/badge - -# The test fum.api.tests.ApiTestCase.test_user fails if run via the vagrant -# shell provisioner or via ‘vagrant ssh -c 'python manage.py test …'’. -# self.ldap_val('mail', user) returns [] instead of raising KeyError. -# The test succeeds if run via: -# ― ‘vagrant ssh’ followed by ‘python manage.py test …’ -# ― ‘python manage.py test …’ on a dev machine -# ― .travis.yml -# Adding a few environment variable (with any name and value) seems to solve -# the problem. -# -# TODO: understand the cause of this. -Va=a Vb=b Vc=c \ - python manage.py test --settings=fum.settings.test --noinput fum - -./manage.py migrate --noinput -./manage.py collectstatic --noinput - - -# TODO: improve this -# Run 2 processes in the background -PATH=./node_modules/.bin:$PATH ./watcher.py & -REMOTE_USER=`cat vagrant/REMOTE_USER` \ - ./manage.py runserver --nostatic 0.0.0.0:8000 & diff --git a/vagrant/apt-get.sh b/vagrant/apt-get.sh deleted file mode 100755 index 03703a9..0000000 --- a/vagrant/apt-get.sh +++ /dev/null @@ -1,20 +0,0 @@ -#! /usr/bin/env bash - -set -u # exit if using uninitialised variable -set -e # exit if some command in this script fails -trap "echo $0 failed because a command in the script failed" ERR - -apt-get update -apt-get install -y \ - build-essential htop \ - python-dev python-pip python-virtualenv \ - libxml2-dev libxslt1-dev \ - libcurl4-openssl-dev libssl-dev zlib1g-dev libpcre3-dev \ - libldap2-dev libsasl2-dev \ - libjpeg-dev \ - libfreetype6-dev \ - postgresql libpq-dev \ - npm \ - memcached - -ln -s /usr/bin/nodejs /usr/bin/node diff --git a/vagrant/postgresql.sh b/vagrant/postgresql.sh deleted file mode 100755 index b9dfd42..0000000 --- a/vagrant/postgresql.sh +++ /dev/null @@ -1,13 +0,0 @@ -#! /usr/bin/env bash - -set -u # exit if using uninitialised variable -set -e # exit if some command in this script fails -trap "echo $0 failed because a command in the script failed" ERR - -sudo -u postgres createuser vagrant -sudo -u postgres psql -c 'alter user vagrant with createdb' postgres - -# fragile: disable password -sed -e 's|^host all all 127.0.0.1/32 md5$|host all all 127.0.0.1/32 trust|' -i /etc/postgresql/9.*/main/pg_hba.conf - -service postgresql restart diff --git a/vagrant/provision-root.sh b/vagrant/provision-root.sh deleted file mode 100755 index 40b47e6..0000000 --- a/vagrant/provision-root.sh +++ /dev/null @@ -1,15 +0,0 @@ -#! /usr/bin/env bash - -set -u # exit if using uninitialised variable -set -e # exit if some command in this script fails -trap "echo $0 failed because a command in the script failed" ERR - -ROOT_DIR=/vagrant -VAGRANT_DIR="$ROOT_DIR"/vagrant - -"$VAGRANT_DIR"/apt-get.sh -"$VAGRANT_DIR"/postgresql.sh - -# To run ‘datamigrate’ at provision-*.sh time, we need these commands -# which also run at always-*.sh time. -pip install -r "$ROOT_DIR"/requirements.txt diff --git a/vagrant/provision-user.sh b/vagrant/provision-user.sh deleted file mode 100755 index 79745e8..0000000 --- a/vagrant/provision-user.sh +++ /dev/null @@ -1,21 +0,0 @@ -#! /usr/bin/env bash - -set -u # exit if using uninitialised variable -set -e # exit if some command in this script fails -trap "echo $0 failed because a command in the script failed" ERR - - -createdb fum - -# To run ‘datamigrate’ at provision-*.sh time, we need these commands -# which also run at always-*.sh time. -cd /vagrant -mkdir -p media/portraits/full media/portraits/thumb media/portraits/badge - -# see the comment in always-user.sh for the environment variables -Va=a Vb=b Vc=c \ - python manage.py test --settings=fum.settings.test --noinput fum - -./manage.py migrate --noinput - -./manage.py datamigrate