This is an automated email from the ASF dual-hosted git repository. kbhatt pushed a commit to branch branch-2.0 in repository https://gitbox.apache.org/repos/asf/atlas.git
commit cc6cac35686eb0c09c85f7c5d0f5e17c49516855 Author: Nikhil Bonte <[email protected]> AuthorDate: Thu Feb 13 15:34:11 2020 -0800 ATLAS-3604: Data Migration status display. Signed-off-by: Ashutosh Mestry <[email protected]> --- dashboardv2/gruntfile.js | 3 +- dashboardv2/public/js/migration.js | 163 +++++++++++++++++++++ .../js/templates/migration/MigrationView_tmpl.html | 17 +++ .../public/js/views/migration/MigrationView.js | 68 +++++++++ dashboardv2/public/js/views/site/Statistics.js | 50 ++++--- dashboardv2/public/migration-status.html.tpl | 80 ++++++++++ .../atlas/web/filters/ActiveServerFilter.java | 25 ++++ .../atlas/web/security/AtlasSecurityConfig.java | 1 + 8 files changed, 383 insertions(+), 24 deletions(-) diff --git a/dashboardv2/gruntfile.js b/dashboardv2/gruntfile.js index f9c9579..efb1475 100644 --- a/dashboardv2/gruntfile.js +++ b/dashboardv2/gruntfile.js @@ -287,7 +287,8 @@ module.exports = function(grunt) { } }, files: { - [distPath + '/index.html']: [modulesPath + 'index.html.tpl'] + [distPath + '/index.html']: [modulesPath + 'index.html.tpl'], + [distPath + '/migration-status.html']: [modulesPath + 'migration-status.html.tpl'] } } } diff --git a/dashboardv2/public/js/migration.js b/dashboardv2/public/js/migration.js new file mode 100644 index 0000000..e49a69f --- /dev/null +++ b/dashboardv2/public/js/migration.js @@ -0,0 +1,163 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +require.config({ + /* starting point for application */ + 'hbs': { + 'disableI18n': true, // This disables the i18n helper and doesn't require the json i18n files (e.g. en_us.json) + 'helperPathCallback': // Callback to determine the path to look for helpers + function(name) { // ('/template/helpers/'+name by default) + return 'modules/Helpers'; + }, + 'templateExtension': 'html', // Set the extension automatically appended to templates + 'compileOptions': {} // options object which is passed to Handlebars compiler + }, + 'urlArgs': "bust=" + getBustValue(), + /** + * Requested as soon as the loader has processed the configuration. It does + * not block any other require() calls from starting their requests for + * modules, it is just a way to specify some modules to load asynchronously + * as part of a config block. + * @type {Array} An array of dependencies to load. + */ + 'deps': ['marionette'], + + /** + * The number of seconds to wait before giving up on loading a script. + * @default 7 seconds + * @type {Number} + */ + 'waitSeconds': 30, + + 'shim': { + 'backbone': { + 'deps': ['underscore', 'jquery'], + 'exports': 'Backbone' + }, + 'bootstrap': { + 'deps': ['jquery'], + 'exports': 'jquery' + }, + 'underscore': { + 'exports': '_' + }, + 'marionette': { + 'deps': ['backbone'] + }, + 'hbs': { + 'deps': ['underscore', 'handlebars'] + }, + 'jquery-placeholder': { + 'deps': ['jquery'] + }, + 'jquery-ui': { + 'deps': ['jquery'] + }, + 'moment-timezone': { + 'deps': ['moment'] + }, + 'moment': { + 'exports': ['moment'] + }, + 'pnotify': { + 'exports': ['pnotify'] + }, + 'jstree': { + 'deps': ['jquery'] + }, + 'd3': { + 'exports': ['d3'] + } + }, + + paths: { + 'jquery': 'libs/jquery/js/jquery.min', + 'underscore': 'libs/underscore/underscore-min', + 'bootstrap': 'libs/bootstrap/js/bootstrap.min', + 'backbone': 'libs/backbone/backbone-min', + 'backbone.babysitter': 'libs/backbone.babysitter/lib/backbone.babysitter.min', + 'marionette': 'libs/backbone-marionette/backbone.marionette.min', + 'backbone.paginator': 'libs/backbone-paginator/backbone.paginator.min', + 'tmpl': 'templates', + 'requirejs.text': 'libs/requirejs-text/text', + 'handlebars': 'external_lib/require-handlebars-plugin/js/handlebars', + 'hbs': 'external_lib/require-handlebars-plugin/js/hbs', + 'i18nprecompile': 'external_lib/require-handlebars-plugin/js/i18nprecompile', + 'jquery-placeholder': 'libs/jquery-placeholder/js/jquery.placeholder', + 'platform': 'libs/platform/platform', + 'pnotify': 'external_lib/pnotify/pnotify.custom.min', + 'pnotify.buttons': 'external_lib/pnotify/pnotify.custom.min', + 'pnotify.confirm': 'external_lib/pnotify/pnotify.custom.min', + 'moment': 'libs/moment/js/moment.min', + 'moment-timezone': 'libs/moment-timezone/moment-timezone-with-data.min', + 'jquery-ui': 'external_lib/jquery-ui/jquery-ui.min', + 'jstree': 'libs/jstree/jstree.min', + 'd3': 'libs/d3/d3.min' + }, + + /** + * If set to true, an error will be thrown if a script loads that does not + * call define() or have a shim exports string value that can be checked. + * To get timely, correct error triggers in IE, force a define/shim export. + * @type {Boolean} + */ + 'enforceDefine': false +}); + +require([ + 'marionette', + 'utils/Helper', + 'bootstrap' +], function(Marionette, Helper) { + var that = this; + var App = new Marionette.Application(); + + App.addRegions({ + rContent: '.page-wrapper' + }); + + App.addInitializer(function() { + Backbone.history.start(); + }); + var Router = Backbone.Router.extend({ + routes: { + // Define some URL routes + "": "defaultAction", + // Default + "*actions": "defaultAction" + }, + initialize: function(options) {}, + showRegions: function() {}, + execute: function(callback, args) { + this.preRouteExecute(); + if (callback) callback.apply(this, args); + this.postRouteExecute(); + }, + preRouteExecute: function() {}, + postRouteExecute: function(name, args) {}, + defaultAction: function() { + require(["views/migration/MigrationView"], function(MigrationView) { + App.rContent.show(new MigrationView()); + }); + } + }); + App.appRouter = new Router({ + entityDefCollection: this.entityDefCollection, + }); + App.start(); +}); \ No newline at end of file diff --git a/dashboardv2/public/js/templates/migration/MigrationView_tmpl.html b/dashboardv2/public/js/templates/migration/MigrationView_tmpl.html new file mode 100644 index 0000000..41a665f --- /dev/null +++ b/dashboardv2/public/js/templates/migration/MigrationView_tmpl.html @@ -0,0 +1,17 @@ +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<div id="r_statisticsView" style="padding-top:30px;"></div> \ No newline at end of file diff --git a/dashboardv2/public/js/views/migration/MigrationView.js b/dashboardv2/public/js/views/migration/MigrationView.js new file mode 100644 index 0000000..8f4b0b0 --- /dev/null +++ b/dashboardv2/public/js/views/migration/MigrationView.js @@ -0,0 +1,68 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +define(['require', + 'backbone', + 'hbs!tmpl/migration/MigrationView_tmpl' +], function(require, Backbone, MigrationViewTmpl) { + 'use strict'; + + var ProfileLayoutView = Backbone.Marionette.LayoutView.extend( + /** @lends ProfileLayoutView */ + { + _viewName: 'MigrationView', + + template: MigrationViewTmpl, + + /** Layout sub regions */ + regions: { + RStatisticsView: "#r_statisticsView", + }, + /** ui selector cache */ + ui: {}, + /** ui events hash */ + events: function() {}, + /** + * intialize a new ProfileLayoutView Layout + * @constructs + */ + initialize: function(options) { + this.apiBaseUrl = this.getBaseUrl(window.location.pathname); + }, + bindEvents: function() {}, + getBaseUrl: function(url) { + var path = url.replace(/\/[\w-]+.(jsp|html)|\/+$/ig, ''), + splitPath = path.split("/"); + if (splitPath && splitPath[splitPath.length - 1] === "n") { + splitPath.pop(); + return splitPath.join("/"); + } + return path; + }, + onRender: function() { + var that = this; + require(["views/site/Statistics", "collection/VTagList", "utils/UrlLinks"], function(Statistics, VTagList, UrlLinks) { + that.metricCollection = new VTagList(); + that.metricCollection.url = UrlLinks.metricsApiUrl(); + that.metricCollection.modelAttrName = "data"; + that.RStatisticsView.show(new Statistics({ hideModal: false, metricCollection: that.metricCollection })); + }) + } + }); + return ProfileLayoutView; +}); \ No newline at end of file diff --git a/dashboardv2/public/js/views/site/Statistics.js b/dashboardv2/public/js/views/site/Statistics.js index 75e11b0..d670ca7 100644 --- a/dashboardv2/public/js/views/site/Statistics.js +++ b/dashboardv2/public/js/views/site/Statistics.js @@ -67,28 +67,30 @@ define(['require', _.extend(this, options); var that = this; this.DATA_MAX_LENGTH = 25; - var modal = new Modal({ - title: 'Statistics', - content: this, - okCloses: true, - okText: "Close", - showFooter: true, - allowCancel: false, - width: "60%", - headerButtons: [{ - title: "Refresh Data", - btnClass: "fa fa-refresh", - onClick: function() { - modal.$el.find('.header-button .fa-refresh').tooltip('hide').prop('disabled', true).addClass('fa-spin'); - that.fetchMetricData({ update: true }); - } - }] - }); - modal.on('closeModal', function() { - modal.trigger('cancel'); - }); - this.modal = modal; - modal.open(); + if (this.hideModal !== false) { + var modal = new Modal({ + title: 'Statistics', + content: this, + okCloses: true, + okText: "Close", + showFooter: true, + allowCancel: false, + width: "60%", + headerButtons: [{ + title: "Refresh Data", + btnClass: "fa fa-refresh", + onClick: function() { + modal.$el.find('.header-button .fa-refresh').tooltip('hide').prop('disabled', true).addClass('fa-spin'); + that.fetchMetricData({ update: true }); + } + }] + }).open(); + + modal.on('closeModal', function() { + modal.trigger('cancel'); + }); + this.modal = modal; + } }, bindEvents: function() { var that = this; @@ -110,7 +112,9 @@ define(['require', that.$('.statsContainer,.statsNotificationContainer').removeClass('hide'); that.$('.statsLoader,.statsNotificationLoader').removeClass('show'); if (options && options.update) { - that.modal.$el.find('.header-button .fa-refresh').prop('disabled', false).removeClass('fa-spin'); + if (that.modal) { + that.modal.$el.find('.header-button .fa-refresh').prop('disabled', false).removeClass('fa-spin'); + } Utils.notifySuccess({ content: "Metric data is refreshed" }) diff --git a/dashboardv2/public/migration-status.html.tpl b/dashboardv2/public/migration-status.html.tpl new file mode 100644 index 0000000..0048826 --- /dev/null +++ b/dashboardv2/public/migration-status.html.tpl @@ -0,0 +1,80 @@ +<!DOCTYPE html> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]--> +<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]--> +<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]--> +<!--[if gt IE 7]> + <script src="js/external_lib/es5-shim.min.js?bust=<%- bust %>"></script> + <script src="js/external_lib/respond.min.js?bust=<%- bust %>"></script> +<![endif]--> +<!--[if gt IE 8]><!--> +<html class="no-js"> +<!--<![endif]--> + +<head> + <meta charset="utf-8" /> + <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8;" /> + <title>Atlas</title> + <meta name="description" content="" /> + <meta name="viewport" content="width=device-width" /> + <link rel="shortcut icon" href="img/favicon.ico?bust=<%- bust %>" type="image/x-icon" /> + <link rel="icon" href="img/favicon.ico?bust=<%- bust %>" type="image/x-icon" /> + <!-- Place favicon.ico and apple-touch-icon.png in the root directory --> + <link rel="stylesheet" type="text/css" href="css/animate.min.css?bust=<%- bust %>" /> + <link rel="stylesheet" href="js/libs/backgrid/css/backgrid.css?bust=<%- bust %>" /> + <link rel="stylesheet" href="js/libs/backgrid-filter/css/backgrid-filter.min.css?bust=<%- bust %>" /> + <link rel="stylesheet" href="js/libs/backgrid-paginator/css/backgrid-paginator.css?bust=<%- bust %>" /> + <link rel="stylesheet" href="js/libs/backgrid-orderable-columns/css/backgrid-orderable-columns.css?bust=<%- bust %>" /> + <link rel="stylesheet" href="js/libs/backgrid-sizeable-columns/css/backgrid-sizeable-columns.css?bust=<%- bust %>" /> + <link rel="stylesheet" href="js/external_lib/backgrid-columnmanager/css/Backgrid.ColumnManager.css?bust=<%- bust %>" /> + <link rel="stylesheet" href="js/libs/select2/css/select2.min.css?bust=<%- bust %>" /> + <link rel="stylesheet" href="js/libs/bootstrap/css/bootstrap.min.css?bust=<%- bust %>" /> + <link rel="stylesheet" href="js/libs/jquery-asBreadcrumbs/css/asBreadcrumbs.min.css?bust=<%- bust %>" /> + <link rel="stylesheet" href="css/googlefonts.css?bust=<%- bust %>" type="text/css" /> + <link rel="stylesheet" type="text/css" href="js/external_lib/jquery-ui/jquery-ui.min.css?bust=<%- bust %>" /> + <link href="css/bootstrap-sidebar.css?bust=<%- bust %>" rel="stylesheet" /> + <link href="js/libs/font-awesome/css/font-awesome.min.css?bust=<%- bust %>" rel="stylesheet" /> + <link href="js/external_lib/pnotify/pnotify.custom.min.css?bust=<%- bust %>" rel="stylesheet" /> + <link href="js/libs/jQueryQueryBuilder/css/query-builder.default.min.css?bust=<%- bust %>" rel="stylesheet" /> + <link href="js/libs/bootstrap-daterangepicker/css/daterangepicker.css?bust=<%- bust %>" rel="stylesheet" /> + <link rel="stylesheet" href="js/libs/nvd3/css/nv.d3.min.css?bust=<%- bust %>" /> + <link href="js/libs/jstree/css/default/default-theme.min.css?bust=<%- bust %>" rel="stylesheet" /> + <link href="js/libs/pretty-checkbox/css/pretty-checkbox.min.css?bust=<%- bust %>" rel="stylesheet" /> + <link href="css/style.css?bust=<%- bust %>" rel="stylesheet" /> +</head> + +<body> + <div class="container-fluid"> + <div class="col-sm-12"> + <div class="page-wrapper"> + <div class="initialLoading"></div> + </div> + </div> + </div> + <!-- build:js scripts/main.js --> + <script data-main="js/migration.js?bust=<%- bust %>" src="js/libs/requirejs/require.js?bust=<%- bust %>"></script> + <!-- endbuild --> + <script type="text/javascript"> + var getBustValue = function() { + return "<%- bust %>"; + }; + </script> +</body> + +</html> \ No newline at end of file diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/ActiveServerFilter.java b/webapp/src/main/java/org/apache/atlas/web/filters/ActiveServerFilter.java index 4d452e4..2c28aaf 100644 --- a/webapp/src/main/java/org/apache/atlas/web/filters/ActiveServerFilter.java +++ b/webapp/src/main/java/org/apache/atlas/web/filters/ActiveServerFilter.java @@ -50,6 +50,8 @@ import java.io.IOException; public class ActiveServerFilter implements Filter { private static final Logger LOG = LoggerFactory.getLogger(ActiveServerFilter.class); + private static final String MIGRATION_STATUS_STATIC_PAGE = "migration-status.html"; + private final ActiveInstanceState activeInstanceState; private ServiceState serviceState; @@ -88,6 +90,9 @@ public class ActiveServerFilter implements Filter { LOG.error("Instance in transition. Service may not be ready to return a result"); httpServletResponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); } else if (serviceState.isInstanceInMigration()) { + if (isRootURI(servletRequest)) { + handleMigrationRedirect(servletRequest, servletResponse); + } HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; LOG.error("Instance in migration. Service may not be ready to return a result"); httpServletResponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); @@ -123,10 +128,30 @@ public class ActiveServerFilter implements Filter { } } + private boolean isRootURI(ServletRequest servletRequest) { + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + String requestURI = httpServletRequest.getRequestURI(); + return requestURI.equals("/"); + } + boolean isInstanceActive() { return serviceState.getState() == ServiceState.ServiceStateValue.ACTIVE; } + private void handleMigrationRedirect(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException { + + HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + + String redirectLocation = httpServletRequest.getRequestURL() + MIGRATION_STATUS_STATIC_PAGE; + if (isUnsafeHttpMethod(httpServletRequest)) { + httpServletResponse.setHeader(HttpHeaders.LOCATION, redirectLocation); + httpServletResponse.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT); + } else { + httpServletResponse.sendRedirect(redirectLocation); + } + } + private void handleRedirect(HttpServletRequest servletRequest, HttpServletResponse httpServletResponse, String activeServerAddress) throws IOException { String requestURI = servletRequest.getRequestURI(); diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java index 46b70ac..e74a9e9 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java @@ -170,6 +170,7 @@ public class AtlasSecurityConfig extends WebSecurityConfigurerAdapter { "/js/**", "/n/js/**", "/ieerror.html", + "/migration-status.html", "/api/atlas/admin/status", "/api/atlas/admin/metrics"));
