diff --git a/.gitignore b/.gitignore index adafb7a59c..5678a7be76 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ awx/ui/static awx/ui/build_test awx/ui/client/languages awx/ui/templates/ui/index.html +awx/ui/templates/ui/installing.html # Tower setup playbook testing setup/test/roles/postgresql diff --git a/awx/main/middleware.py b/awx/main/middleware.py index dfb08b20fb..2cae0961c3 100644 --- a/awx/main/middleware.py +++ b/awx/main/middleware.py @@ -9,11 +9,13 @@ import six from django.conf import settings from django.contrib.auth.models import User from django.db.models.signals import post_save -from django.db import IntegrityError +from django.db.migrations.executor import MigrationExecutor +from django.db import IntegrityError, connection from django.utils.functional import curry -from django.shortcuts import get_object_or_404 +from django.shortcuts import get_object_or_404, redirect from django.apps import apps from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import reverse from awx.main.models import ActivityStream from awx.api.authentication import TokenAuthentication @@ -162,3 +164,12 @@ class URLModificationMiddleware(object): if request.path_info != new_path: request.path = request.path.replace(request.path_info, new_path) request.path_info = new_path + + +class MigrationRanCheckMiddleware(object): + + def process_request(self, request): + executor = MigrationExecutor(connection) + plan = executor.migration_plan(executor.loader.graph.leaf_nodes()) + if bool(plan) and 'migrations_notran' not in request.path: + return redirect(reverse("ui:migrations_notran")) diff --git a/awx/settings/production.py b/awx/settings/production.py index 1d93ad43e6..7ad7a908b2 100644 --- a/awx/settings/production.py +++ b/awx/settings/production.py @@ -87,6 +87,8 @@ settings_files = os.path.join(settings_dir, '*.py') settings_file = os.environ.get('AWX_SETTINGS_FILE', '/etc/tower/settings.py') +MIDDLEWARE_CLASSES = ('awx.main.middleware.MigrationRanCheckMiddleware',) + MIDDLEWARE_CLASSES + # Attempt to load settings from /etc/tower/settings.py first, followed by # /etc/tower/conf.d/*.py. try: diff --git a/awx/sso/middleware.py b/awx/sso/middleware.py index 71e93725e9..7690dbd0ce 100644 --- a/awx/sso/middleware.py +++ b/awx/sso/middleware.py @@ -34,7 +34,7 @@ class SocialAuthMiddleware(SocialAuthExceptionMiddleware): if not hasattr(request, 'successful_authenticator'): request.successful_authenticator = None - if not request.path.startswith('/sso/'): + if not request.path.startswith('/sso/') and 'migrations_notran' not in request.path: # If token isn't present but we still have a user logged in via Django # sessions, log them out. diff --git a/awx/ui/build/webpack.base.js b/awx/ui/build/webpack.base.js index e56cd0810d..5d565a334d 100644 --- a/awx/ui/build/webpack.base.js +++ b/awx/ui/build/webpack.base.js @@ -25,6 +25,8 @@ const APP_ENTRY = path.join(SOURCE_PATH, 'app.js'); const VENDOR_ENTRY = path.join(SOURCE_PATH, 'vendor.js'); const INDEX_ENTRY = path.join(CLIENT_PATH, 'index.template.ejs'); const INDEX_OUTPUT = path.join(UI_PATH, 'templates/ui/index.html'); +const INSTALL_RUNNING_ENTRY = path.join(CLIENT_PATH, 'installing.template.ejs') +const INSTALL_RUNNING_OUTPUT = path.join(UI_PATH, 'templates/ui/installing.html'); const THEME_ENTRY = path.join(LIB_PATH, 'theme', 'index.less'); const OUTPUT = 'js/[name].[hash].js'; const CHUNKS = ['vendor', 'app']; @@ -168,6 +170,19 @@ let base = { moduleA.files.sort((fileA, fileB) => fileA.includes('js') ? -1 : 1) moduleB.files.sort((fileA, fileB) => fileA.includes('js') ? -1 : 1) + return moduleA.names[0] === 'vendor' ? -1 : 1 + } + }), + new HtmlWebpackPlugin({ + alwaysWriteToDisk: true, + template: INSTALL_RUNNING_ENTRY, + filename: INSTALL_RUNNING_OUTPUT, + inject: false, + chunks: CHUNKS, + chunksSortMode: (moduleA, moduleB) => { + moduleA.files.sort((fileA, fileB) => fileA.includes('js') ? -1 : 1) + moduleB.files.sort((fileA, fileB) => fileA.includes('js') ? -1 : 1) + return moduleA.names[0] === 'vendor' ? -1 : 1 } }) diff --git a/awx/ui/client/installing.template.ejs b/awx/ui/client/installing.template.ejs new file mode 100644 index 0000000000..f9005def42 --- /dev/null +++ b/awx/ui/client/installing.template.ejs @@ -0,0 +1,35 @@ + + + + {% load staticfiles %} + + + + + + <% htmlWebpackPlugin.files.css.forEach(file => {%> + + <% }) %> + <% htmlWebpackPlugin.files.js.forEach(file => {%> + + <% }) %> + + + +
+
+
+ +

AWX is Upgrading

+

+ AWX is currently upgrading or installing, this page will refresh when done. +

+
+
+
+ + diff --git a/awx/ui/urls.py b/awx/ui/urls.py index ea4bf96291..f92787d28d 100644 --- a/awx/ui/urls.py +++ b/awx/ui/urls.py @@ -6,5 +6,6 @@ from django.conf.urls import * urlpatterns = patterns('awx.ui.views', url(r'^$', 'index', name='index'), + url(r'^migrations_notran/$', 'migrations_notran', name='migrations_notran'), url(r'^portal/$', 'portal_redirect', name='portal_redirect'), ) diff --git a/awx/ui/views.py b/awx/ui/views.py index 0a47154615..8c1d2fb7a3 100644 --- a/awx/ui/views.py +++ b/awx/ui/views.py @@ -20,3 +20,9 @@ class PortalRedirectView(RedirectView): url = '/#/portal' portal_redirect = PortalRedirectView.as_view() + +class MigrationsNotran(TemplateView): + + template_name = 'ui/installing.html' + +migrations_notran = MigrationsNotran.as_view()