diff --git a/components/app-core/backend/info.go b/components/app-core/backend/info.go index b1a69ffd2b..b1b40093bd 100644 --- a/components/app-core/backend/info.go +++ b/components/app-core/backend/info.go @@ -23,6 +23,7 @@ type Info struct { User *interfaces.ConnectedUser `json:"user"` Endpoints map[string]map[string]*Endpoint `json:"endpoints"` CloudFoundry *interfaces.CFInfo `json:"cloud-foundry,omitempty"` + PluginConfig map[string]string `json:"plugin-config,omitempty"` } func (p *portalProxy) info(c echo.Context) error { @@ -59,6 +60,7 @@ func (p *portalProxy) getInfo(c echo.Context) (*Info, error) { User: uaaUser, Endpoints: make(map[string]map[string]*Endpoint), CloudFoundry: p.Config.CloudFoundryInfo, + PluginConfig: p.Config.PluginConfig, } // initialize the Endpoints maps for _, plugin := range p.Plugins { diff --git a/components/app-core/backend/main.go b/components/app-core/backend/main.go index 8eba804a49..873108f40a 100644 --- a/components/app-core/backend/main.go +++ b/components/app-core/backend/main.go @@ -305,6 +305,7 @@ func loadPortalConfig(pc interfaces.PortalConfig) (interfaces.PortalConfig, erro // Add custom properties pc.CFAdminIdentifier = CFAdminIdentifier pc.HTTPS = true + pc.PluginConfig = make(map[string]string) return pc, nil } diff --git a/components/app-core/backend/repository/interfaces/structs.go b/components/app-core/backend/repository/interfaces/structs.go index 62ce711d3a..8293d862ed 100644 --- a/components/app-core/backend/repository/interfaces/structs.go +++ b/components/app-core/backend/repository/interfaces/structs.go @@ -102,4 +102,5 @@ type PortalConfig struct { LoginHook LoginHookFunc SessionStore SessionStorer ConsoleConfig *ConsoleConfig + PluginConfig map[string]string } diff --git a/components/app-core/frontend/src/utils/logged-in.service.js b/components/app-core/frontend/src/utils/logged-in.service.js index 46116a9a28..c41bf26d7b 100644 --- a/components/app-core/frontend/src/utils/logged-in.service.js +++ b/components/app-core/frontend/src/utils/logged-in.service.js @@ -26,7 +26,7 @@ var loggedIn = false; var lastUserInteraction = moment(); - var sessionChecker, dialog; + var sessionChecker, dialog, dashboardRouteFunc; // Check the session every 30 seconds (Note: this is vey cheap to do unless the session is about to expire) var checkSessionInterval = 30 * 1000; @@ -147,7 +147,9 @@ return { isLoggedIn: isLoggedIn, - userInteracted: userInteracted + userInteracted: userInteracted, + setDashboardRouteFunc: setDashboardRouteFunc, + getDashboardRoute: getDashboardRoute }; function isLoggedIn() { @@ -158,6 +160,17 @@ lastUserInteraction = moment(); } + function setDashboardRouteFunc(func) { + dashboardRouteFunc = func; + } + + function getDashboardRoute() { + // At this point console-info must have been called + if (angular.isFunction(dashboardRouteFunc)) { + return dashboardRouteFunc(); + } + } + function _getAccountModel() { return modelManager.retrieve('app.model.account'); } diff --git a/components/app-core/frontend/src/view/application.directive.js b/components/app-core/frontend/src/view/application.directive.js index ea963cf1a7..6392fd2807 100644 --- a/components/app-core/frontend/src/view/application.directive.js +++ b/components/app-core/frontend/src/view/application.directive.js @@ -26,18 +26,19 @@ * @namespace app.view.application.ApplicationController * @memberof app.view.application * @name ApplicationController + * @param {object} $q - Angular $q service * @param {app.utils.appEventService} appEventService - the event bus service * @param {app.model.modelManager} modelManager - the application model manager * @param {app.model.loginManager} loginManager - the login management service * @param {app.view.appUpgradeCheck} appUpgradeCheck - the upgrade check service * @param {object} appLocalStorage - the Local Storage In Service - * @param {object} appUtilsService - the App Utils service * @param {app.view.consoleSetupCheck} consoleSetupCheck - the Console Setup checkservice * @param {object} $timeout - Angular $timeout service * @param {$stateParams} $stateParams - Angular ui-router $stateParams service * @param {$window} $window - Angular $window service * @param {$rootScope} $rootScope - Angular $rootScope service * @param {$scope} $scope - Angular $scope service + * @param {appLoggedInService} appLoggedInService - Logged In Service * @property {app.utils.appEventService} appEventService - the event bus service * @property {app.model.modelManager} modelManager - the application model manager * @property {app.view.appUpgradeCheck} appUpgradeCheck - the upgrade check service @@ -47,8 +48,9 @@ * @property {boolean} serverErrorOnLogin - a flag indicating if user login failed because of a server error. * @class */ - function ApplicationController(appEventService, modelManager, loginManager, appUpgradeCheck, appLocalStorage, - appUtilsService, consoleSetupCheck, $timeout, $stateParams, $window, $rootScope, $scope) { + function ApplicationController($q, appEventService, modelManager, loginManager, appUpgradeCheck, appLocalStorage, + consoleSetupCheck, $timeout, $stateParams, $window, $rootScope, $scope, + appLoggedInService) { var vm = this; @@ -63,7 +65,6 @@ vm.hideNavigation = $stateParams.hideNavigation; vm.hideAccount = $stateParams.hideAccount; vm.navbarIconsOnly = false; - vm.isEndpointsDashboardAvailable = appUtilsService.isPluginAvailable('endpointsDashboard'); vm.login = login; vm.logout = logout; @@ -184,61 +185,49 @@ */ var account = modelManager.retrieve('app.model.account'); - // Fetch the list of services instances + var serviceInstanceModel = modelManager.retrieve('app.model.serviceInstance'); + var userServiceInstanceModel = modelManager.retrieve('app.model.serviceInstance.user'); /* eslint-disable */ // TODO: If this fails, we should show a notification message /* eslint-enable */ - modelManager.retrieve('app.model.serviceInstance') - .list() - .then(function onSuccess(data) { - var noEndpoints = data.numAvailable === 0; - // Admin - if (account.isAdmin()) { - // Go the endpoints dashboard if there are no CF clusters - if (noEndpoints) { - vm.redirectState = 'endpoint.dashboard'; - if (!vm.isEndpointsDashboardAvailable) { - appEventService.$emit(appEventService.events.TRANSFER, 'error-page', {error: 'notSetup'}); - continueLogin = false; - } + $q.all([ + serviceInstanceModel.list(), + modelManager.retrieve('app.model.consoleInfo').getConsoleInfo(), + userServiceInstanceModel.list() + ]) + .then(function () { + var cfOnly = _.filter(serviceInstanceModel.serviceInstances, {cnsi_type: 'cf'}) || []; + var noEndpoints = cfOnly.length === 0; + var dashboardRedirect = appLoggedInService.getDashboardRoute(); + + if (noEndpoints) { + // Admin + if (account.isAdmin()) { + vm.redirectState = dashboardRedirect; } - } else { - // Developer or Endpoint Dashboard plugin isn't loaded - if (noEndpoints) { + if (!vm.redirectState) { // No CF instances, so the system is not setup and the user can't fix this continueLogin = false; appEventService.$emit(appEventService.events.TRANSFER, 'error-page', {error: 'notSetup'}); - } else { - var userServiceInstanceModel = modelManager.retrieve('app.model.serviceInstance.user'); - // Need to get the user's service list to determine if they have any connected - return userServiceInstanceModel.list().then(function () { - // Developer - allow user to connect services, if we have some and none are connected - if (userServiceInstanceModel.getNumValid() === 0) { - vm.redirectState = 'endpoint.dashboard'; - if (!vm.isEndpointsDashboardAvailable) { - appEventService.$emit(appEventService.events.TRANSFER, 'error-page', {error: 'notConnected'}); - continueLogin = false; - } - } - }); + } + } else { + // Need to get the user's service list to determine if they have any connected + // Developer - allow user to connect services, if we have some and none are connected + if (userServiceInstanceModel.getNumValid() === 0) { + vm.redirectState = dashboardRedirect; + if (!vm.redirectState) { + appEventService.$emit(appEventService.events.TRANSFER, 'error-page', {error: 'notConnected'}); + continueLogin = false; + } } } }) - .then(function () { - // Update consoleInfo - return modelManager.retrieve('app.model.consoleInfo').getConsoleInfo(); - }) - .then(function () { - // Get the user registered services once at login - only refreshed in endpoints dashboard - var userServiceInstanceModel = modelManager.retrieve('app.model.serviceInstance.user'); - return userServiceInstanceModel.list(); - }) .finally(function () { vm.showGlobalSpinner = false; if (continueLogin) { // When we notify listeners that login has completed, in some cases we don't want them - // to redirect tp their page - we might want to control that the go to the endpoints dahsboard (for exmample). - // So, we pass this flag to tell them login happenned, but that they should not redirect + // to redirect to their page - we might want to control that they go to the endpoints dashboard (for example). + // So, we pass this flag to tell them login happened, but that they should not redirect appEventService.$emit(appEventService.events.LOGIN, !!vm.redirectState); // We need to do this after the login events are handled, so that ui-router states we might go to are registered if (vm.redirectState) { diff --git a/components/app-core/frontend/test/unit/view/application.directive.spec.js b/components/app-core/frontend/test/unit/view/application.directive.spec.js index c630516d59..3eee943e98 100644 --- a/components/app-core/frontend/test/unit/view/application.directive.spec.js +++ b/components/app-core/frontend/test/unit/view/application.directive.spec.js @@ -27,7 +27,6 @@ applicationCtrl = $element.controller('application'); $httpBackend.when('GET', '/pp/v1/proxy/v2/info').respond(200, {}); $httpBackend.when('GET', '/pp/v1/proxy/v2/apps?page=1&results-per-page=48').respond(200, {guid: {}}); - $httpBackend.when('GET', '/pp/v1/cnsis/registered').respond([]); // For some tests, the redirected state depends on whether the endpoints dashboard is available redirectStateName = $state.get('endpoint.dashboard') ? 'endpoint.dashboard' : 'error-page'; @@ -98,10 +97,11 @@ $httpBackend.when('POST', '/pp/v1/auth/login/uaa').respond(200, {account: 'dev', scope: 'foo'}); $httpBackend.when('GET', '/pp/v1/cnsis').respond(200, []); $httpBackend.when('GET', '/pp/v1/info').respond(200, {}); + $httpBackend.when('GET', '/pp/v1/cnsis/registered').respond(200, []); $httpBackend.expectPOST('/pp/v1/auth/login/uaa'); // No endpoints are set up, so we should go to error page - $httpBackend.expectGET('/pp/v1/cnsis/registered').respond(200, []); + $httpBackend.expectGET('/pp/v1/cnsis/registered'); applicationCtrl.login('dev', 'dev'); $httpBackend.flush(); @@ -241,7 +241,7 @@ expect($state.current.name).toBe(redirectStateName); }); - it('should not show cluster registration if cluster count > 0', function () { + it('should go to endpoints dashboard if cluster count > 0 and none connected', function () { var responseData = [ {id: 1, name: 'name', api_endpoint: {Scheme: 'http', Host: 'api.host.com'}, cnsi_type: 'cf'} ]; @@ -253,6 +253,27 @@ applicationCtrl.login('admin', 'admin'); $httpBackend.flush(); + expect(applicationCtrl.redirectState).toBe('endpoint.dashboard'); + expect(applicationCtrl.showGlobalSpinner).toBe(false); + }); + + it('should not go to endpoints dashboard if cluster count > 0 and at least one connected', function () { + var responseData = [ + {id: 1, name: 'name', api_endpoint: {Scheme: 'http', Host: 'api.host.com'}, cnsi_type: 'cf'} + ]; + $httpBackend.when('GET', '/pp/v1/cnsis') + .respond(200, responseData); + + var future = 50000 + (new Date()).getTime() / 1000; + + $httpBackend.when('GET', '/pp/v1/info').respond(200, []); + $httpBackend.when('GET', '/pp/v1/cnsis/registered').respond(200, [ + { account: 'test', token_expiry: future, guid: 'service', cnsi_type: 'cf', name: 'test', api_endpoint: testAptEndpoint } + ]); + + applicationCtrl.login('admin', 'admin'); + $httpBackend.flush(); + expect(applicationCtrl.redirectState).toBe(false); expect(applicationCtrl.showGlobalSpinner).toBe(false); }); @@ -267,7 +288,7 @@ ]); }); - it('should show service instance registration if we dont not have registered services', function () { + it('should show service instance registration if we don\'t have registered services', function () { $httpBackend.when('GET', '/pp/v1/cnsis').respond(200, []); $httpBackend.when('GET', '/pp/v1/info').respond(200, []); $httpBackend.when('GET', '/pp/v1/cnsis/registered').respond(200, []); @@ -275,10 +296,27 @@ applicationCtrl.login('dev', 'dev'); $httpBackend.flush(); + expect(applicationCtrl.redirectState).toBe('endpoint.dashboard'); expect(applicationCtrl.showGlobalSpinner).toBe(false); }); - it('should not show service instance registration if we have registered services', function () { + it('should show service instance registration if we don\'t have connected services', function () { + var responseData = [ + {id: 1, name: 'name', api_endpoint: {Scheme: 'http', Host: 'api.host.com'}, cnsi_type: 'cf'} + ]; + $httpBackend.when('GET', '/pp/v1/cnsis') + .respond(200, responseData); + $httpBackend.when('GET', '/pp/v1/info').respond(200, []); + $httpBackend.when('GET', '/pp/v1/cnsis/registered').respond(200, []); + + applicationCtrl.login('dev', 'dev'); + $httpBackend.flush(); + + expect(applicationCtrl.redirectState).toBe('endpoint.dashboard'); + expect(applicationCtrl.showGlobalSpinner).toBe(false); + }); + + it('should not show service instance registration if we have connected services', function () { var future = 50000 + (new Date()).getTime() / 1000; $httpBackend.when('GET', '/pp/v1/info').respond(200, []); @@ -289,6 +327,7 @@ applicationCtrl.login('dev', 'dev'); $httpBackend.flush(); + expect(applicationCtrl.redirectState).toBe(false); expect(applicationCtrl.showGlobalSpinner).toBe(false); }); }); diff --git a/components/cloud-foundry-hosting/backend/main.go b/components/cloud-foundry-hosting/backend/main.go index df746bd495..d45d4e9e48 100644 --- a/components/cloud-foundry-hosting/backend/main.go +++ b/components/cloud-foundry-hosting/backend/main.go @@ -20,10 +20,11 @@ import ( ) const ( - VCapApplication = "VCAP_APPLICATION" - CFApiURLOverride = "CF_API_URL" - CFApiForceSecure = "CF_API_FORCE_SECURE" - cfSessionCookieName = "JSESSIONID" + VCapApplication = "VCAP_APPLICATION" + CFApiURLOverride = "CF_API_URL" + CFApiForceSecure = "CF_API_FORCE_SECURE" + cfSessionCookieName = "JSESSIONID" + ForceEndpointDashboard = "FORCE_ENDPOINT_DASHBOARD" ) type CFHosting struct { @@ -100,6 +101,23 @@ func (ch *CFHosting) Init() error { log.Info("No forced override to HTTPS") } + disableEndpointDashboard := true + if config.IsSet(ForceEndpointDashboard) { + // Force the Endpoint Dashboard to be visible? + if forceStr, err := config.GetValue(ForceEndpointDashboard); err == nil { + if force, err := strconv.ParseBool(forceStr); err == nil { + disableEndpointDashboard = !force + } + } + } + + if disableEndpointDashboard { + log.Info("Endpoint Dashboard has been DISABLED") + } else { + log.Info("Endpoint Dashboard has been ENABLED") + } + ch.portalProxy.GetConfig().PluginConfig["endpointsDashboardDisabled"] = strconv.FormatBool(disableEndpointDashboard) + log.Infof("Using Cloud Foundry API URL: %s", appData.API) cfEndpointSpec, _ := ch.portalProxy.GetEndpointTypeSpec("cf") newCNSI, _, err := cfEndpointSpec.Info(appData.API, true) diff --git a/components/cloud-foundry/frontend/test/e2e/application-wall.spec.js b/components/cloud-foundry/frontend/test/e2e/application-wall.spec.js index 541f7d3760..786b763425 100644 --- a/components/cloud-foundry/frontend/test/e2e/application-wall.spec.js +++ b/components/cloud-foundry/frontend/test/e2e/application-wall.spec.js @@ -19,6 +19,7 @@ appWallConfigNoClusters(ngMockE2E.$httpBackend); helpers.setBrowserNormal(); helpers.loadApp(); + applicationWall.showApplications(); }); afterAll(function () { diff --git a/components/endpoints-dashboard/i18n/en_US/dashboard.json b/components/endpoints-dashboard/i18n/en_US/dashboard.json index 556bf99b89..17570b90d0 100644 --- a/components/endpoints-dashboard/i18n/en_US/dashboard.json +++ b/components/endpoints-dashboard/i18n/en_US/dashboard.json @@ -13,6 +13,7 @@ "start": "To enable developers to use the [[@:console]] to interact with [[@:product.name]],", "link": "please register endpoints.", "end": "The process of registering these endpoints will make them discoverable for anyone who logs into the [[@:product.console]]. Once you register an endpoint, we recommend connecting it to your account so you can manage it.", + "admin": "To access your cloud native workloads and other related third party services, connect with your personal credentials to the corresponding registered services.", "non-admin": "To access your cloud native workloads and other related third party services, connect with your personal credentials to the corresponding registered services. Registered services are provided by your systems administrator." }, "no-endpoints": { diff --git a/components/endpoints-dashboard/src/endpoints-dashboard.module.js b/components/endpoints-dashboard/src/endpoints-dashboard.module.js index a904ec776d..4d9aef4001 100644 --- a/components/endpoints-dashboard/src/endpoints-dashboard.module.js +++ b/components/endpoints-dashboard/src/endpoints-dashboard.module.js @@ -3,30 +3,51 @@ angular .module('endpoints-dashboard', []) + .constant('endpointsDashboardDisabledKey', 'endpointsDashboardDisabled') .run(register); - function register($q, $state, modelManager, appEventService, appUtilsService) { - return new EndpointsDashboard($q, $state, modelManager, appEventService, appUtilsService); + function register($q, $state, modelManager, appEventService, appUtilsService, + endpointsDashboardDisabledKey, appLoggedInService) { + return new EndpointsDashboard($q, $state, modelManager, appEventService, appUtilsService, + endpointsDashboardDisabledKey, appLoggedInService); } - function EndpointsDashboard($q, $state, modelManager, appEventService, appUtilsService) { + function EndpointsDashboard($q, $state, modelManager, appEventService, appUtilsService, endpointsDashboardDisabledKey, + appLoggedInService) { var initialized = $q.defer(); - function init() { - return initialized.promise; - } + appLoggedInService.setDashboardRouteFunc(getDashboardRoute); appEventService.$on(appEventService.events.LOGIN, function () { - onLoggedIn(); + if (isDashboardDisabled()) { + delete env.plugins.endpointsDashboard; + } else { + onLoggedIn(); + } }); appUtilsService.chainStateResolve('endpoint', $state, init); + function init() { + return initialized.promise; + } + function onLoggedIn() { var menu = modelManager.retrieve('app.model.navigation').menu; menu.addMenuItem('endpoints', 'endpoint.dashboard', 'menu.endpoints', 2, 'settings_input_component'); initialized.resolve(); } + + function getDashboardRoute() { + if (!isDashboardDisabled()) { + return 'endpoint.dashboard'; + } + } + + function isDashboardDisabled() { + var info = modelManager.retrieve('app.model.consoleInfo').info; + return _.get(info, 'plugin-config.' + endpointsDashboardDisabledKey) === 'true'; + } } })(); diff --git a/components/endpoints-dashboard/src/view/view.html b/components/endpoints-dashboard/src/view/view.html index 1f88d3691a..85441b2291 100644 --- a/components/endpoints-dashboard/src/view/view.html +++ b/components/endpoints-dashboard/src/view/view.html @@ -16,13 +16,17 @@ class="endpoint-notification panel panel-default alert" ng-class="{'endpoint-notification-slim': endpointsDashboardCtrl.endpoints.length === 0}"> -
- endpoints-dashboard.intro.start - endpoints-dashboard.intro.link - endpoints-dashboard.intro.end +
+
endpoints-dashboard.intro.admin
+
endpoints-dashboard.intro.non-admin
-
- endpoints-dashboard.intro.non-admin +
+ +
+ endpoints-dashboard.intro.start + endpoints-dashboard.intro.link + endpoints-dashboard.intro.end +
0; + vm.endpointsConnected = !!_.find(vm.endpoints, {connected: 'connected'}); + vm.showWelcomeMessage = !vm.endpointsConnected; } } diff --git a/components/endpoints-dashboard/test/e2e/po/endpoints-dashboard.spec.js b/components/endpoints-dashboard/test/e2e/po/endpoints-dashboard.spec.js index abf5ef355f..01c738e753 100644 --- a/components/endpoints-dashboard/test/e2e/po/endpoints-dashboard.spec.js +++ b/components/endpoints-dashboard/test/e2e/po/endpoints-dashboard.spec.js @@ -1,20 +1,23 @@ (function () { 'use strict'; - var helpers = require('../../../../app-core/frontend/test/e2e/po/helpers.po'); - var resetTo = require('../../../../app-core/frontend/test/e2e/po/resets.po'); - var loginPage = require('../../../../app-core/frontend/test/e2e/po/login-page.po'); + var appCore = '../../../../app-core/frontend/'; + var cloudFoundry = '../../../../cloud-foundry/frontend/'; + + var helpers = require(appCore + 'test/e2e/po/helpers.po'); + var resetTo = require(appCore + 'test/e2e/po/resets.po'); + var loginPage = require(appCore + 'test/e2e/po/login-page.po'); var endpointsPage = require('./endpoints/endpoints-dashboard.po.js'); var registerEndpoint = require('./endpoints/register-endpoint.po.js'); - var navbar = require('../../../../app-core/frontend/test/e2e/po/navbar.po'); - var actionMenu = require('../../../../app-core/frontend/test/e2e/po/widgets/actions-menu.po'); - var confModal = require('../../../../app-core/frontend/test/e2e/po/widgets/confirmation-modal.po'); + var navbar = require(appCore + 'test/e2e/po/navbar.po'); + var actionMenu = require(appCore + 'test/e2e/po/widgets/actions-menu.po'); + var confModal = require(appCore + 'test/e2e/po/widgets/confirmation-modal.po'); var _ = require('lodash'); // These are pretty tied into the tests. The dashboard will need to know about specific endpoints to determine // behaviour on log in, out, etc. These could, if a problem, be split out into the cf app if this becomes a problem - var cfHelpers = require('../../../../cloud-foundry/frontend/test/e2e/po/helpers.po'); - var applications = require('../../../../cloud-foundry/frontend/test/e2e/po/applications/applications.po'); + var cfHelpers = require(cloudFoundry + 'test/e2e/po/helpers.po'); + var applications = require(cloudFoundry + 'test/e2e/po/applications/applications.po'); describe('Endpoints Dashboard -', function () { @@ -54,11 +57,16 @@ resetToLoggedIn(resetTo.resetAllCnsi, true); }); - it('Should reach application wall with \'no clusters\' message after log in', function () { + it('Should reach endpoint dashboard after log in', function () { + expect(endpointsPage.isEndpoints()).toBeTruthy(); + }); + + it('Should show application wall with \'no clusters\' message', function () { + applications.showApplications(); expect(applications.isApplicationWallNoClusters()).toBeTruthy(); }); - it('Click on link to reach endpoints', function () { + it('Click on application wall link to reach endpoints', function () { applications.clickEndpointsDashboard() .then(function () { return endpointsPage.isEndpoints(); @@ -68,8 +76,8 @@ }); }); - it('Welcome message should not be displayed', function () { - expect(endpointsPage.welcomeMessage().isPresent()).toBeFalsy(); + it('Welcome message should be displayed', function () { + expect(endpointsPage.welcomeMessage().isPresent()).toBeTruthy(); }); }); @@ -535,14 +543,14 @@ }); }); - it('should go directly to applications view on logout and login (as admin)', function () { + it('should go directly to endpoint view on logout and login (as admin)', function () { // This would be better in the 'non admin' section, however it's easier to test here with a service registered // This removes the need to go through/test the endpoint dashboard registration process alongside this test navbar.logout(); loginPage.waitForLogin(); loginPage.login(helpers.getAdminUser(), helpers.getAdminPassword()); - expect(browser.getCurrentUrl()).toBe(helpers.getHost() + '/#/cf/applications/list/gallery-view'); + expect(browser.getCurrentUrl()).toBe(helpers.getHost() + '/#/endpoint'); }); it('should go directly to applications view on logout and login', function () { diff --git a/components/endpoints-dashboard/test/e2e/po/endpoints/endpoints-dashboard.po.js b/components/endpoints-dashboard/test/e2e/po/endpoints/endpoints-dashboard.po.js index 48160d816c..2ce54423c1 100644 --- a/components/endpoints-dashboard/test/e2e/po/endpoints/endpoints-dashboard.po.js +++ b/components/endpoints-dashboard/test/e2e/po/endpoints/endpoints-dashboard.po.js @@ -1,10 +1,12 @@ (function () { 'use strict'; + var appCore = '../../../../../app-core/frontend/'; + var _ = require('lodash'); - var navbar = require('../../../../../app-core/frontend/test/e2e/po/navbar.po'); - var helpers = require('../../../../../app-core/frontend/test/e2e/po/helpers.po'); - var actionMenu = require('../../../../../app-core/frontend/test/e2e/po/widgets/actions-menu.po'); + var navbar = require(appCore + 'test/e2e/po/navbar.po'); + var helpers = require(appCore + 'test/e2e/po/helpers.po'); + var actionMenu = require(appCore + 'test/e2e/po/widgets/actions-menu.po'); var credentialsFormHelper = require('../widgets/credentials-form.po'); module.exports = { diff --git a/components/endpoints-dashboard/test/e2e/po/widgets/credentials-form.po.js b/components/endpoints-dashboard/test/e2e/po/widgets/credentials-form.po.js index 735c9c4e17..ac8c2a7622 100644 --- a/components/endpoints-dashboard/test/e2e/po/widgets/credentials-form.po.js +++ b/components/endpoints-dashboard/test/e2e/po/widgets/credentials-form.po.js @@ -1,9 +1,11 @@ (function () { 'use strict'; + var appCore = '../../../../../app-core/frontend/'; + // Service instances registration helpers - var helpers = require('../../../../../app-core/frontend/test/e2e/po/helpers.po'); - var asyncDialog = require('../../../../../app-core/frontend/test/e2e/po/widgets/async-dialog.po'); + var helpers = require(appCore + 'test/e2e/po/helpers.po'); + var asyncDialog = require(appCore + 'test/e2e/po/widgets/async-dialog.po'); var credentialsFormName = 'form.registerCnsi'; module.exports = { diff --git a/deploy/cloud-foundry/README.md b/deploy/cloud-foundry/README.md index d918d5340b..a796213cf1 100644 --- a/deploy/cloud-foundry/README.md +++ b/deploy/cloud-foundry/README.md @@ -10,6 +10,9 @@ cd stratos-ui cf push ``` +>**NOTE** The console will pre-configure the host Cloud Foundry endpoint. No other CF instance can be registered unless the instructions in the section 'Enable Endpoints Dashboard to register additional Cloud Foundry endpoints' are followed. + All other deployment methods (helm, docker-compose, docker all-in-one, etc) allow the registration of multiple CF instances by default. + You will then be able to open a web browser and navigate to the console URL: `https://console.` @@ -23,9 +26,9 @@ If you run into issues, please refer to the [Troubleshooting Guide](#troubleshoo Note: 1. You need the cf CLI command line tool installed and available on the path. -1. You need to have configured the cf cli to point to your Cloud Foundry cluster, to be authenticated with your credentials and to be targeted at the organization and space where you want the console application be created. -1. You may need to configure Application Security Groups on your Cloud Foundry Cluster in order that Stratos UI can communicate with the Cloud Foundry API. See [below](#application-security-groups) for more information. -1. The Stratos UI Console will automatically detect the API endpoint for your Cloud Foundry. To do so, it relies on the `cf_api_url` value inside the `VCAP_APPLICATION` environment variable. If this is not provided by your Cloud Foundry platform, then you must manually update the application manifest as described [below](#console-fails-to-start). +2. You need to have configured the cf cli to point to your Cloud Foundry cluster, to be authenticated with your credentials and to be targeted at the organization and space where you want the console application be created. +3. You may need to configure Application Security Groups on your Cloud Foundry Cluster in order that Stratos UI can communicate with the Cloud Foundry API. See [below](#application-security-groups) for more information. +4. The Stratos UI Console will automatically detect the API endpoint for your Cloud Foundry. To do so, it relies on the `cf_api_url` value inside the `VCAP_APPLICATION` environment variable. If this is not provided by your Cloud Foundry platform, then you must manually update the application manifest as described [below](#console-fails-to-start). ## Troubleshooting @@ -101,7 +104,6 @@ If the `cf_api` environment variable is absent then set the `CF_API_URL` variabl However, if the `cf_api` environment variable is present, and an HTTP address is specified, it is possible that insecure traffic may be blocked. See the following _Setting the `CF_API_FORCE_SECURE` env variable in the manifest_ section. - #### Setting the `CF_API_URL` env variable in the manifest To specify the Cloud Foundry API endpoint, add the `CF_API_URL` variable to the manifest, for example: @@ -135,3 +137,28 @@ applications: env: CF_API_FORCE_SECURE: true ``` + +### Enable Endpoints Dashboard to register additional Cloud Foundry endpoints + +>**NOTE** This method is meant to demonstrate the capabilities of the console with multiple endpoints and is not meant for production environments + +This method comes with two caveats. + +1. The console will lose stored data when a cf app instance is restarted +2. Multiple instances of the app will contain multiple separate stored data instances. This will mean the user may connect to a different one with a different storage when revisiting the console. + + +To enable the dashboard add the environment variable 'FORCE_ENDPOINT_DASHBOARD' to the manifest before the call to 'cf push' is made. For example + +``` +applications: +- name: console + memory: 768M + disk_quota: 1G + host: console + timeout: 180 + buildpack: https://github.com/cloudfoundry-incubator/multi-buildpack + health-check-type: port + env: + FORCE_ENDPOINT_DASHBOARD: true +``` diff --git a/deploy/cloud-foundry/package.sh b/deploy/cloud-foundry/package.sh index eb30c4a2e8..6c39f08f7e 100755 --- a/deploy/cloud-foundry/package.sh +++ b/deploy/cloud-foundry/package.sh @@ -21,9 +21,6 @@ sed '2 a"cloud-foundry-hosting",' ${TOP_LEVEL}/plugins.json.bk > ${TOP_LEVEL}/pl find . -name glide.lock -exec sed -i '/^testImports.*/q' {} \; find . -name glide.lock -exec sed -i 's/^testImports:$/testImports: []/g' {} \; -# Delete endpoints-dashboard from bower.json -sed -i '/"endpoints-dashboard.*/d' bower.json - npm install -g gulp bower cd ${TOP_LEVEL} diff --git a/docs/development.md b/docs/development.md index 512016d293..ec14897767 100644 --- a/docs/development.md +++ b/docs/development.md @@ -153,6 +153,13 @@ $ gulp dev In both cases the console should be available via https://localhost:3100 +> **Note:** If you see the following error when running 'gulp dev' you may need to increase your OS ulimit. +``` +Error: ENFILE: file table overflow, scandir ; + at Error (native) +-bash: /dev/null: Too many open files in system +``` + ### Linting We use eslint to executing linting. To run these execute... ```