From: Josh Farwell Date: Fri, 19 Jun 2015 04:26:38 +0000 (-0700) Subject: Created web::install class X-Git-Url: https://gerrit.linuxfoundation.org/infra/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F81%2F181%2F2;p=puppet%2Fmodules%2Fmailman3.git Created web::install class Mailman3::web::install creates a service user, creates needed files and directories, and installs Postorius and Hyperkitty to a virtualenv with their dependencies. We are installing the Django project files from mailman-bundler and including them in the module. Change-Id: I88eda2d7d6ffe81a0a874c16fb3b82edabe0e904 Signed-off-by: Josh Farwell --- diff --git a/.fixtures.yml b/.fixtures.yml index 1615b00..df2e0f8 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -3,8 +3,13 @@ fixtures: #forge_modules: # stdlib: "puppetlabs/stdlib" repositories: + epel: + repo: "https://github.com/stahnma/puppet-module-epel.git" + ref: "7322b80c268087bc4b967a9a6ca29923853971f8" firewall: "git://github.com/puppetlabs/puppetlabs-firewall.git" - stdlib: "git://github.com/puppetlabs/puppetlabs-stdlib.git" postgresql: "git://github.com/puppetlabs/puppetlabs-postgresql.git" + python: "git://github.com/stankevich/puppet-python.git" + stdlib: "git://github.com/puppetlabs/puppetlabs-stdlib.git" + uwsgi: "git://github.com/jfpdx/puppet-uwsgi.git" symlinks: mailman3: "#{source_dir}" diff --git a/files/djangoproject/mailman_web/__init__.py b/files/djangoproject/mailman_web/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/files/djangoproject/mailman_web/production.py b/files/djangoproject/mailman_web/production.py new file mode 100644 index 0000000..90184a9 --- /dev/null +++ b/files/djangoproject/mailman_web/production.py @@ -0,0 +1,372 @@ +#-*- coding: utf-8 -*- +""" +Django settings for HyperKitty + Postorius +""" + +import os +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +VAR_DIR = "/var/spool" + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'change-that-at-install-time' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = False + +TEMPLATE_DEBUG = DEBUG + +ADMINS = ( + ('Mailman Admin', 'root@localhost'), +) + +# Hosts/domain names that are valid for this site; required if DEBUG is False +# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts +ALLOWED_HOSTS = ["localhost"] +# And for BrowserID too, see +# http://django-browserid.rtfd.org/page/user/settings.html#django.conf.settings.BROWSERID_AUDIENCES +BROWSERID_AUDIENCES = [ "http://localhost", "http://localhost:8000" ] + +# Mailman API credentials +MAILMAN_REST_SERVER = MAILMAN_API_URL = 'http://localhost:8001' +MAILMAN_API_USER = MAILMAN_USER = 'restadmin' +MAILMAN_API_PASS = MAILMAN_PASS = 'restpass' +MAILMAN_ARCHIVER_KEY = 'SecretArchiverAPIKey' +MAILMAN_ARCHIVER_FROM = ('127.0.0.1', '::1', '::ffff:127.0.0.1') + +# Application definition + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + #'django.contrib.sites', + 'django.contrib.messages', + 'django.contrib.staticfiles', + # Uncomment the next line to enable the admin: + 'django.contrib.admin', + # Uncomment the next line to enable admin documentation: + # 'django.contrib.admindocs', + 'hyperkitty', + 'social.apps.django_app.default', + 'rest_framework', + 'django_gravatar', + 'crispy_forms', + 'paintstore', + 'compressor', + 'django_browserid', + 'haystack', + 'django_extensions', + 'postorius', +) + + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + # Uncomment the next line for simple clickjacking protection: + # 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'hyperkitty.middleware.SSLRedirect', + 'hyperkitty.middleware.TimezoneMiddleware', +) + +ROOT_URLCONF = 'mailman_web.urls' + +# CSS theme for postorius +MAILMAN_THEME = "default" + + +# Database +# https://docs.djangoproject.com/en/1.6/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', # Last part is one of 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. + 'NAME': 'mailmanweb', # Example, change as needed + 'USER': 'mailmanweb', # Example, change as needed + 'PASSWORD': 'change-this-password', # Example, obviously + 'HOST': '127.0.0.1', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. + 'PORT': '', # Set to empty string for default. + } +} + + +# If you're behind a proxy, use the X-Forwarded-Host header +# See https://docs.djangoproject.com/en/1.5/ref/settings/#use-x-forwarded-host +#USE_X_FORWARDED_HOST = True +# And if your proxy does your SSL encoding for you, set SECURE_PROXY_SSL_HEADER +# see https://docs.djangoproject.com/en/1.5/ref/settings/#secure-proxy-ssl-header +#SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') + +# Internationalization +# https://docs.djangoproject.com/en/1.6/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'America/Chicago' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.6/howto/static-files/ + +# Absolute filesystem path to the directory that will hold user-uploaded files. +# Example: "/var/www/example.com/media/" +MEDIA_ROOT = '' + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash. +# Examples: "http://example.com/media/", "http://media.example.com/" +MEDIA_URL = '' + +# Absolute path to the directory static files should be collected to. +# Don't put anything in this directory yourself; store your static files +# in apps' "static/" subdirectories and in STATICFILES_DIRS. +# Example: "/var/www/example.com/static/" +#STATIC_ROOT = '' +STATIC_ROOT = os.path.join(VAR_DIR, "mailman-web", "static") + +# URL prefix for static files. +# Example: "http://example.com/static/", "http://static.example.com/" +STATIC_URL = '/static/' + +# Additional locations of static files +STATICFILES_DIRS = ( + # Put strings here, like "/home/html/static" or "C:/www/django/static". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. +) + +# List of finder classes that know how to find static files in +# various locations. +STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', +# 'django.contrib.staticfiles.finders.DefaultStorageFinder', + 'compressor.finders.CompressorFinder', +) + + +TEMPLATE_CONTEXT_PROCESSORS = ( + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "django.core.context_processors.debug", + "django.core.context_processors.i18n", + "django.core.context_processors.media", + "django.core.context_processors.static", + "django.core.context_processors.csrf", + "django.core.context_processors.request", + "django.core.context_processors.tz", + "django.contrib.messages.context_processors.messages", + "social.apps.django_app.context_processors.backends", + "social.apps.django_app.context_processors.login_redirect", + "hyperkitty.context_processors.export_settings", + "hyperkitty.context_processors.postorius_info", + "postorius.context_processors.postorius", +) + +TEMPLATE_DIRS = ( + # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. +) + +# Django 1.6+ defaults to a JSON serializer, but it won't work with django-openid, see +# https://bugs.launchpad.net/django-openid-auth/+bug/1252826 +SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer' + + +LOGIN_URL = '/archives/accounts/login/' +LOGIN_REDIRECT_URL = '/archives/' +LOGIN_ERROR_URL = '/archives/accounts/login/' + +BROWSERID_USERNAME_ALGO = lambda email: email # Use the email as identifier +BROWSERID_VERIFY_CLASS = "django_browserid.views.Verify" + + + +# +# Social auth +# + +AUTHENTICATION_BACKENDS = ( + #'social.backends.open_id.OpenIdAuth', + # http://python-social-auth.readthedocs.org/en/latest/backends/google.html + 'social.backends.google.GoogleOpenId', + #'social.backends.google.GoogleOAuth2', + #'social.backends.twitter.TwitterOAuth', + 'social.backends.yahoo.YahooOpenId', + 'django_browserid.auth.BrowserIDBackend', + 'django.contrib.auth.backends.ModelBackend', +) + +SOCIAL_AUTH_USERNAME_IS_FULL_EMAIL = True + +# http://python-social-auth.readthedocs.org/en/latest/pipeline.html#authentication-pipeline +SOCIAL_AUTH_PIPELINE = ( + 'social.pipeline.social_auth.social_details', + 'social.pipeline.social_auth.social_uid', + 'social.pipeline.social_auth.auth_allowed', + 'social.pipeline.social_auth.social_user', + 'social.pipeline.user.get_username', + # Associates the current social details with another user account with + # a similar email address. Disabled by default, enable with care: + # http://python-social-auth.readthedocs.org/en/latest/use_cases.html#associate-users-by-email + #'social.pipeline.social_auth.associate_by_email', + 'social.pipeline.user.create_user', + 'social.pipeline.social_auth.associate_user', + 'social.pipeline.social_auth.load_extra_data', + 'social.pipeline.user.user_details', +) + + + +# +# Gravatar +# https://github.com/twaddington/django-gravatar +# +# Gravatar base url. +#GRAVATAR_URL = 'http://cdn.libravatar.org/' +# Gravatar base secure https url. +#GRAVATAR_SECURE_URL = 'https://seccdn.libravatar.org/' +# Gravatar size in pixels. +#GRAVATAR_DEFAULT_SIZE = '80' +# An image url or one of the following: 'mm', 'identicon', 'monsterid', 'wavatar', 'retro'. +#GRAVATAR_DEFAULT_IMAGE = 'mm' +# One of the following: 'g', 'pg', 'r', 'x'. +#GRAVATAR_DEFAULT_RATING = 'g' +# True to use https by default, False for plain http. +#GRAVATAR_DEFAULT_SECURE = True + +# +# django-compressor +# https://pypi.python.org/pypi/django_compressor +# +COMPRESS_PRECOMPILERS = ( + ('text/less', 'lessc {infile} {outfile}'), +) +COMPRESS_OFFLINE = True +# needed for debug mode +#INTERNAL_IPS = ('127.0.0.1',) + +# Django Crispy Forms +CRISPY_TEMPLATE_PACK = 'bootstrap3' +CRISPY_FAIL_SILENTLY = not DEBUG + + +# +# Full-text search engine +# +HAYSTACK_CONNECTIONS = { + 'default': { + 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', + 'PATH': os.path.join(VAR_DIR, "mailman-web", "fulltext_index"), + }, +} + + +# A sample logging configuration. The only tangible logging +# performed by this configuration is to send an email to +# the site admins on every HTTP 500 error when DEBUG=False. +# See http://docs.djangoproject.com/en/dev/topics/logging for +# more details on how to customize your logging configuration. +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'filters': { + 'require_debug_false': { + '()': 'django.utils.log.RequireDebugFalse' + } + }, + 'handlers': { + 'mail_admins': { + 'level': 'ERROR', + 'filters': ['require_debug_false'], + 'class': 'django.utils.log.AdminEmailHandler' + }, + 'file':{ + 'level': 'INFO', + #'class': 'logging.handlers.RotatingFileHandler', + 'class': 'logging.handlers.WatchedFileHandler', + 'filename': '/var/log/mailman-web/mailman-web.log', + 'formatter': 'verbose', + }, + }, + 'loggers': { + #'django.request': { + # 'handlers': ['mail_admins'], + # 'level': 'ERROR', + # 'propagate': True, + #}, + 'django.request': { + 'handlers': ['file'], + 'level': 'ERROR', + 'propagate': True, + }, + 'django': { + 'handlers': ['file'], + 'level': 'ERROR', + 'propagate': True, + }, + 'hyperkitty': { + 'handlers': ['file'], + 'level': 'INFO', + 'propagate': True, + }, + }, + 'formatters': { + 'verbose': { + 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' + }, + 'simple': { + 'format': '%(levelname)s %(message)s' + }, + }, + 'root': { + 'handlers': ['file'], + 'level': 'INFO', + }, +} + + +## Cache: use the local memcached server +#CACHES = { +# 'default': { +# 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', +# 'LOCATION': '127.0.0.1:11211', +# } +#} + + + +# +# HyperKitty-specific +# + +APP_NAME = 'Mailing-list archives' + +# Allow authentication with the internal user database? +# By default, only a login through Persona or your email provider is allowed. +USE_INTERNAL_AUTH = False + +# Use SSL when logged in +USE_SSL = True + +# Only display mailing-lists from the same virtual host as the webserver +FILTER_VHOST = False + +# This is for development purposes +USE_MOCKUPS = False + + +try: + from settings_local import * +except ImportError: + pass diff --git a/files/djangoproject/mailman_web/urls.py b/files/djangoproject/mailman_web/urls.py new file mode 100644 index 0000000..0eeb212 --- /dev/null +++ b/files/djangoproject/mailman_web/urls.py @@ -0,0 +1,15 @@ +from django.conf.urls import patterns, include, url +from django.core.urlresolvers import reverse_lazy +from django.views.generic import RedirectView + +# Comment the next two lines to disable the admin: +from django.contrib import admin +admin.autodiscover() + +urlpatterns = patterns('', + url(r'^$', RedirectView.as_view(url=reverse_lazy('hyperkitty.views.index.index'))), + url(r'^mailman3/', include('postorius.urls')), + url(r'^archives/', include('hyperkitty.urls')), + url(r'', include('social.apps.django_app.urls', namespace='social'), {"SSL": True}), + url(r'', include('django_browserid.urls'), {"SSL": True}), +) diff --git a/files/djangoproject/mailman_web/wsgi.py b/files/djangoproject/mailman_web/wsgi.py new file mode 100644 index 0000000..dbb21d7 --- /dev/null +++ b/files/djangoproject/mailman_web/wsgi.py @@ -0,0 +1,14 @@ +""" +WSGI config for mailman-web project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/ +""" + +# Set the DJANGO_SETTINGS_MODULE environnement variable to the python path to +# your settings module (development or production) + +from django.core.wsgi import get_wsgi_application +application = get_wsgi_application() diff --git a/files/djangoproject/manage.py b/files/djangoproject/manage.py new file mode 100755 index 0000000..170285d --- /dev/null +++ b/files/djangoproject/manage.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mailman_web.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/manifests/params.pp b/manifests/params.pp index 51c4838..f9c9fee 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -88,12 +88,15 @@ class mailman3::params { $web_user = 'mailman3-web' $web_homedir = '/opt/mailman3-web' - $web_vardir = '/opt/mailman3-web/var' - $web_staticdir = '/opt/mailman3-web/var/static' + $web_logfile = '/opt/mailman3-web/mailman3-web.log' + $web_vardir = '/opt/mailman3-web/data' + $web_staticdir = '/opt/mailman3-web/static' $web_security_key = 'GENERATE' $web_browserid_audiences = [ 'http://localhost:8000', 'http://127.0.0.1:8000' ] $web_database_engine = 'django.db.backends.sqlite3' - $web_database_name = "os.path.join(VAR_DIR, 'mailman-web', 'mailman-web.sqlite')" + $web_database_name = "os.path.join(VAR_DIR, 'mailman-web.sqlite')" + $web_haystack_engine = 'haystack.backends.whoosh_backend.WhooshEngine' + $web_haystack_path = "os.path.join(VAR_DIR, 'fulltext_index')" # Mailman Core default options hashes $core_default_allowed_hosts = { @@ -132,14 +135,23 @@ class mailman3::params { # Web options hashes $web_default_options = { - 'VAR_DIR' => $mailman3::params::web_vardir, - 'SECURITY_KEY' => $mailman3::params::web_security_key, - 'BROWSERID_AUDIENCES' => $mailman3::params::web_browserid_audiences, - 'STATIC_ROOT' => $mailman3::params::web_staticdir, - 'DATABASES' => { + 'paths' => { + 'var_dir' => $mailman3::params::web_vardir, + 'static_dir' => $mailman3::params::web_staticdir, + 'log_file' => $mailman3::params::web_logfile, + }, + 'security_key' => $mailman3::params::web_security_key, + 'browserid_audiences' => $mailman3::params::web_browserid_audiences, + 'databases' => { 'default' => { - 'ENGINE' => $mailman3::params::web_database_engine, - 'NAME' => $mailman3::params::web_database_name, + 'engine' => $mailman3::params::web_database_engine, + 'name' => $mailman3::params::web_database_name, + }, + }, + 'haystack_connections' => { + 'default' => { + 'engine' => $mailman3::params::web_haystack_engine, + 'path' => $mailman3::params::web_haystack_path, }, }, } diff --git a/manifests/web/install.pp b/manifests/web/install.pp index 7cb55e1..d5e8bbd 100644 --- a/manifests/web/install.pp +++ b/manifests/web/install.pp @@ -46,4 +46,151 @@ class mailman3::web::install ( $web_homedir, $web_user, ) { + validate_string($django_version) + validate_string($hyperkitty_version) + validate_bool($install_web) + validate_bool($manage_django_packages) + validate_bool($manage_webserver) + validate_hash($options) + validate_string($postorius_version) + validate_absolute_path($web_homedir) + validate_string($web_user) + + # install web server if we are managing web server + if ($manage_webserver) { + include '::uwsgi' + } + + # pulls path out of config data into variables we can use + # if we don't have these keys defined, something is wrong + if has_key($options, 'paths') { + + if has_key($options['paths'], 'var_dir') { + validate_absolute_path($options['paths']['var_dir']) + $web_vardir = $options['paths']['var_dir'] + } + else { fail "No valid var_dir in options['paths']" } + + if has_key($options['paths'], 'static_dir') { + validate_absolute_path($options['paths']['static_dir']) + $web_staticdir = $options['paths']['static_dir'] + } + else { fail "No valid static_dir in options['paths']" } + + if has_key($options['paths'], 'log_file') { + validate_absolute_path($options['paths']['log_file']) + $web_logfile = $options['paths']['log_file'] + } + else { fail "No valid log_file in options['paths']" } + + } + else { fail "No valid 'paths' section in options hash" } + + + if ($install_web) { + + # include python class for pip and virtualenv + include ::python + + # create service user + user { $web_user: + ensure => present, + comment => 'Mailman3 Web Service User', + home => $web_homedir, + managehome => true, + shell => '/bin/bash', + system => true, + } + + # create needed dirs and files from config data + file { [ + $web_vardir, + $web_staticdir, + $web_homedir, + ]: + ensure => directory, + owner => $web_user, + group => $web_user, + require => User[$web_user], + } + + file { $web_logfile: + ensure => file, + owner => $web_user, + group => $web_user, + require => User[$web_user], + } + + # install django production project files from mailman-bundler + file { "${web_homedir}/djangoproject": + ensure => directory, + owner => $web_user, + group => $web_user, + source => 'puppet:///modules/mailman3/djangoproject', + recurse => remote, + require => User[$web_user], + } + + # create virtualenv + python::virtualenv { "${web_homedir}/virtualenv": + ensure => present, + version => 'system', + systempkgs => true, + owner => $web_user, + group => $web_user, + cwd => "${web_homedir}/virtualenv", + timeout => 0, + require => [ + User[$web_user], + Class['::python'], + ], + } + + if ($manage_django_packages) { + python::pip { 'Django': + ensure => $django_version, + virtualenv => "${web_homedir}/virtualenv", + before => [ + Python::Pip['postorius'], + Python::Pip['hyperkitty'], + ], + } + } + else { + warning "If Django has not already been installed in global site-packages, \ +Pip will install it as a dependency automatically." + } + + # install mailman3-web packages + # pip resources autorequire their virtualenvs + python::pip { 'postorius': + ensure => $postorius_version, + virtualenv => "${web_homedir}/virtualenv", + } + + python::pip { 'hyperkitty': + ensure => $hyperkitty_version, + virtualenv => "${web_homedir}/virtualenv", + } + + # TODO: should figure out based on options whether to install these deps + python::pip { 'whoosh': + ensure => present, + virtualenv => "${web_homedir}/virtualenv", + } + + python::pip { 'beautifulsoup': + ensure => present, + virtualenv => "${web_homedir}/virtualenv", + } + + include mailman3::repo + + package { 'nodejs-less': + ensure => installed, + require => Class['mailman3::repo'], + } + + } + } diff --git a/spec/classes/web__install_spec.rb b/spec/classes/web__install_spec.rb new file mode 100644 index 0000000..2c72e6d --- /dev/null +++ b/spec/classes/web__install_spec.rb @@ -0,0 +1,259 @@ +require 'spec_helper' + +describe 'mailman3::web::install', :type => :class do + # uwsgi class freaks out on my machine unless we overwrite facts. + # We're pretending to be Debian to avoid rspec errors originating from the + # epel class, which is included by the python class we are including. + let(:facts) { { + :fdqn => 'my.test.com', + :ipaddress => '10.0.0.42', + :osfamily => 'Debian', + :operatingsystem => '8.0', + } } + + + + # No default params, so we should fail with 'must pass' if empty + context 'with defaults for all parameters' do + let(:params) {{}} + + it do + expect { + should compile + }.to raise_error(RSpec::Expectations::ExpectationNotMetError, + /Must pass /) + end + end + + context 'with good defaults for all parameters' do + + let(:params) { + { + 'django_version' => '1.7', + 'hyperkitty_version' => 'present', + 'install_web' => true, + 'manage_django_packages' => true, + 'manage_webserver' => true, + 'postorius_version' => 'present', + 'web_homedir' => '/opt/mailman3-web', + 'web_user' => 'mailman3-web', + 'options' => { + 'paths' => { + 'var_dir' => '/opt/mailman3-web/data', + 'static_dir' => '/opt/mailman3-web/static', + 'log_file' => '/opt/mailman3-web/mailman3-web.log', + }, + }, + } + } + + it { is_expected.to contain_class('uwsgi') } + it { is_expected.to contain_class('python') } + it { is_expected.to contain_class('mailman3::repo') } + + it { is_expected.to contain_user('mailman3-web').with( + 'home' => '/opt/mailman3-web', + ) } + + it { is_expected.to contain_file('/opt/mailman3-web/data').with( + 'owner' => 'mailman3-web', + 'group' => 'mailman3-web', + ).that_requires('User[mailman3-web]') } + + it { is_expected.to contain_file('/opt/mailman3-web/static').with( + 'owner' => 'mailman3-web', + 'group' => 'mailman3-web', + ).that_requires('User[mailman3-web]') } + + it { is_expected.to contain_file('/opt/mailman3-web/mailman3-web.log').with( + 'owner' => 'mailman3-web', + 'group' => 'mailman3-web', + ).that_requires('User[mailman3-web]') } + + it { is_expected.to contain_file('/opt/mailman3-web/djangoproject').with( + 'owner' => 'mailman3-web', + 'group' => 'mailman3-web', + ).that_requires('User[mailman3-web]') } + + it { is_expected.to contain_python__virtualenv('/opt/mailman3-web/virtualenv').with( + 'owner' => 'mailman3-web', + 'group' => 'mailman3-web', + 'cwd' => '/opt/mailman3-web/virtualenv', + ).that_requires([ 'User[mailman3-web]', 'Class[python]' ] ) } + + it { is_expected.to contain_python__pip('Django').with( + 'ensure' => '1.7', + 'virtualenv' => '/opt/mailman3-web/virtualenv', + ) } + + it { is_expected.to contain_python__pip('postorius').with( + 'ensure' => 'present', + 'virtualenv' => '/opt/mailman3-web/virtualenv', + ) } + + it { is_expected.to contain_python__pip('hyperkitty').with( + 'ensure' => 'present', + 'virtualenv' => '/opt/mailman3-web/virtualenv', + ) } + + it { is_expected.to contain_python__pip('whoosh').with( + 'ensure' => 'present', + 'virtualenv' => '/opt/mailman3-web/virtualenv', + ) } + + it { is_expected.to contain_python__pip('beautifulsoup').with( + 'ensure' => 'present', + 'virtualenv' => '/opt/mailman3-web/virtualenv', + ) } + + it { is_expected.to contain_class('mailman3::repo') } + + it { is_expected.to contain_package('nodejs-less') } + + end + + context 'with install_web flag set to false' do + + let(:params) { + { + 'django_version' => '', + 'hyperkitty_version' => '', + 'install_web' => false, + 'manage_django_packages' => true, + 'manage_webserver' => true, + 'postorius_version' => '', + 'web_homedir' => '/', + 'web_user' => 'test', + 'options' => { + 'paths' => { + 'var_dir' => '/', + 'static_dir' => '/', + 'log_file' => '/', + }, + }, + } + } + + it { is_expected.to contain_class('uwsgi') } + it { is_expected.to_not contain_package('nodejs-less') } + it { is_expected.to_not contain_class('mailman3::repo') } + it { is_expected.to_not contain_class('python') } + it { is_expected.to_not contain_user('test') } + it { is_expected.to_not contain_file('/') } + it { is_expected.to_not contain_file('//djangoproject') } + it { is_expected.to_not contain_python__virtualenv('//virtualenv') } + it { is_expected.to_not contain_python__pip('Django') } + it { is_expected.to_not contain_python__pip('postorius') } + it { is_expected.to_not contain_python__pip('hyperkitty') } + it { is_expected.to_not contain_python__pip('whoosh') } + it { is_expected.to_not contain_python__pip('beautifulsoup') } + + end + + context 'with manage flags set to false' do + + let(:params) { + { + 'django_version' => '1.7', + 'hyperkitty_version' => 'present', + 'install_web' => true, + 'manage_django_packages' => false, + 'manage_webserver' => false, + 'postorius_version' => 'present', + 'web_homedir' => '/opt/mailman3-web', + 'web_user' => 'mailman3-web', + 'options' => { + 'paths' => { + 'var_dir' => '/opt/mailman3-web/data', + 'static_dir' => '/opt/mailman3-web/static', + 'log_file' => '/opt/mailman3-web/mailman3-web.log', + }, + }, + } + } + + it { is_expected.to_not contain_class('uwsgi') } + it { is_expected.to_not contain_python__pip('Django') } + + it { is_expected.to contain_python__pip('postorius') } + + end + + context 'with bad values for paths' do + + let(:params) { + { + 'django_version' => '1.7', + 'hyperkitty_version' => 'present', + 'install_web' => true, + 'manage_django_packages' => false, + 'manage_webserver' => false, + 'postorius_version' => 'present', + 'web_homedir' => '/opt/mailman3-web', + 'web_user' => 'mailman3-web', + 'options' => { + 'paths' => { + 'var_dir' => 'not a path', + 'static_dir' => 'not a path', + 'log_file' => 'not a path', + }, + }, + } + } + + it do + expect { + should compile + }.to raise_error(/"not a path" is not an absolute path. /) + end + end + + context 'with no keys in paths hash' do + + let(:params) { + { + 'django_version' => '1.7', + 'hyperkitty_version' => 'present', + 'install_web' => true, + 'manage_django_packages' => false, + 'manage_webserver' => false, + 'postorius_version' => 'present', + 'web_homedir' => '/opt/mailman3-web', + 'web_user' => 'mailman3-web', + 'options' => { + 'paths' => {}, + }, + } + } + + it do + expect { + should compile + }.to raise_error(/No valid var_dir /) + end + end + + context 'with no keys in options hash' do + + let(:params) { + { + 'django_version' => '1.7', + 'hyperkitty_version' => 'present', + 'install_web' => true, + 'manage_django_packages' => false, + 'manage_webserver' => false, + 'postorius_version' => 'present', + 'web_homedir' => '/opt/mailman3-web', + 'web_user' => 'mailman3-web', + 'options' => {}, + } + } + + it do + expect { + should compile + }.to raise_error(/No valid 'paths' /) + end + end + +end diff --git a/spec/classes/web_spec.rb b/spec/classes/web_spec.rb index 422cf7a..8fc5789 100644 --- a/spec/classes/web_spec.rb +++ b/spec/classes/web_spec.rb @@ -1,6 +1,16 @@ require 'spec_helper' describe 'mailman3::web', :type => :class do + # uwsgi class freaks out on my machine unless we overwrite facts. + + # We're pretending to be Debian to avoid rspec failures from epel module, + # which is a dependency of the python module we're using. + let(:facts) { { + :fdqn => 'my.test.com', + :ipaddress => '10.0.0.42', + :osfamily => 'Debian', + :operatingsystem => '8.0', + } } context 'with defaults for all parameters' do it { is_expected.to contain_class('mailman3::web') }