From b4791031b87455a93b5516e3da0c574a300d4fd8 Mon Sep 17 00:00:00 2001 From: SanketD92 Date: Wed, 14 Jul 2021 16:59:59 -0400 Subject: [PATCH 01/12] feat: editing UI components for appstream --- .../lib/appstream/appstream-sc-service.js | 17 ++- .../plugins/env-sc-connection-url-plugin.js | 20 ++- .../parts/ScEnvSshConnRowExpanded.js | 139 ++++++++++++++++-- .../parts/ScEnvironmentHttpConnections.js | 2 +- .../parts/ScEnvironmentRdpConnectionRow.js | 111 ++++++++------ .../parts/ScEnvironmentSshConnectionRow.js | 5 + .../plugins/plugin-registry.js | 5 +- 7 files changed, 229 insertions(+), 70 deletions(-) diff --git a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/appstream/appstream-sc-service.js b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/appstream/appstream-sc-service.js index 978b36d68b..6be7ecb051 100644 --- a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/appstream/appstream-sc-service.js +++ b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/appstream/appstream-sc-service.js @@ -18,6 +18,7 @@ const Service = require('@aws-ee/base-services-container/lib/service'); const settingKeys = { appStreamImageName: 'appStreamImageName', + isAppStreamEnabled: 'isAppStreamEnabled', }; class AppStreamScService extends Service { @@ -77,6 +78,7 @@ class AppStreamScService extends Service { if (!stackName) { throw this.boom.badRequest( `Expected stack ${stackName} to be associated with the account ${accountId} but found`, + true, ); } @@ -89,7 +91,10 @@ class AppStreamScService extends Service { const { Names: fleetNames } = await appStream.listAssociatedFleets({ StackName: stackName }).promise(); if (!_.includes(fleetNames, fleetName)) { - throw this.boom.badRequest(`Expected fleet ${fleetName} to be associated with the AppStream stack but found`); + throw this.boom.badRequest( + `Expected fleet ${fleetName} to be associated with the AppStream stack but found`, + true, + ); } return { stackName, fleetName }; @@ -111,7 +116,10 @@ class AppStreamScService extends Service { return `${uid}-${sessionSuffix}`.replace(/[^\w+=,.@-]+/g, '').slice(0, 32); } - async urlForFirefox(requestContext, { environmentId }) { + async getStreamingUrl(requestContext, { environmentId, applicationId }) { + const isAppStreamEnabled = this.settings.get(settingKeys.isAppStreamEnabled); + if (!isAppStreamEnabled) return undefined; + const environmentScService = await this.service('environmentScService'); const appStream = await environmentScService.getClientSdkWithEnvMgmtRole( @@ -132,7 +140,7 @@ class AppStreamScService extends Service { FleetName: fleetName, StackName: stackName, UserId: this.generateUserId(requestContext, environment), - ApplicationId: 'Firefox', + ApplicationId: applicationId, }) .promise(); @@ -143,6 +151,9 @@ class AppStreamScService extends Service { } async urlForRemoteDesktop(requestContext, { environmentId, instanceId }) { + const isAppStreamEnabled = this.settings.get(settingKeys.isAppStreamEnabled); + if (!isAppStreamEnabled) return undefined; + const [environmentScKeypairService, environmentScService] = await this.service([ 'environmentScKeypairService', 'environmentScService', diff --git a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/env-sc-connection-url-plugin.js b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/env-sc-connection-url-plugin.js index 7aff9ac287..9d2f985dcd 100644 --- a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/env-sc-connection-url-plugin.js +++ b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/env-sc-connection-url-plugin.js @@ -19,24 +19,33 @@ async function createConnectionUrl({ envId, connection }, { requestContext, cont const log = await container.find('log'); // Only wraps web urls via app stream (i.e., scheme = 'http' or 'https' or no scheme) const isHttp = connection.scheme === 'http' || connection.scheme === 'https' || _.isEmpty(connection.scheme); + const appStreamScService = await container.find('appStreamScService'); // Only wrap via AppStream if the connection.url exists let appStreamUrl; if (isHttp && connection.url) { log.debug({ - msg: `Target connection URL ${connection.url} will be made available for pasting into AppStream`, + msg: `Target connection URL ${connection.url} will be accessible via AppStream URL`, connection, }); - const appStreamScService = await container.find('appStreamScService'); - appStreamUrl = await appStreamScService.urlForFirefox(requestContext, { + appStreamUrl = await appStreamScService.getStreamingUrl(requestContext, { environmentId: envId, + applicationId: 'Firefox', + }); + } else if (connection.scheme === 'ssh') { + log.debug({ + msg: `Target instance ${connection.instanceId} will be available for SSH connection via AppStream URL`, + connection, + }); + appStreamUrl = await appStreamScService.getStreamingUrl(requestContext, { + environmentId: envId, + applicationId: 'Notepad', }); } else if (connection.scheme === 'rdp') { log.debug({ msg: `Will stream target RDP connection for instance ${connection.instanceId} via AppStream`, connection, }); - const appStreamScService = await container.find('appStreamScService'); appStreamUrl = await appStreamScService.urlForRemoteDesktop(requestContext, { environmentId: envId, instanceId: connection.instanceId, @@ -44,11 +53,10 @@ async function createConnectionUrl({ envId, connection }, { requestContext, cont } if (appStreamUrl) { - connection.scheme = 'https'; // Retain the original destination URL so we don't have to trigger another API call connection.appstreamDestinationUrl = connection.url; - // Now rewrite connection.url to the AppStream streaming URL so it can opened in a new tab + // Now rewrite connection.url to the AppStream streaming URL so it can be opened in a new tab connection.url = appStreamUrl; log.debug({ msg: `Modified connection to use AppStream streaming URL ${connection.url}`, connection }); } diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js index 706c7ec2c7..e0c320cd17 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js @@ -3,14 +3,22 @@ import React from 'react'; import { decorate, action, runInAction, observable, computed } from 'mobx'; import { observer, inject } from 'mobx-react'; import { withRouter } from 'react-router-dom'; -import { Table, List, Segment, Label, Grid } from 'semantic-ui-react'; +import { Table, List, Segment, Label, Grid, Button } from 'semantic-ui-react'; + +import { displayError } from '@aws-ee/base-ui/dist/helpers/notification'; import CopyToClipboard from '../../helpers/CopyToClipboard'; +const openWindow = (url, windowFeatures) => { + return window.open(url, '_blank', windowFeatures); +}; + // expected props // networkInterfaces (via props) // keyName (via props) // connectionId (via props) +// appStreamUrl (via props) +// environment (via props) class ScEnvSshConnRowExpanded extends React.Component { constructor(props) { super(props); @@ -19,6 +27,7 @@ class ScEnvSshConnRowExpanded extends React.Component { this.countDown = undefined; this.intervalId = undefined; this.expired = false; + this.processingId = ''; }); } @@ -28,13 +37,18 @@ class ScEnvSshConnRowExpanded extends React.Component { const result = []; _.forEach(entries, item => { - if (item.publicDnsName) result.push({ value: item.publicDnsName, type: 'dns', scope: 'public', info: 'Public' }); + if (item.publicDnsName && process.env.REACT_APP_IS_APP_STREAM_ENABLED !== 'true') + result.push({ value: item.publicDnsName, type: 'dns', scope: 'public', info: 'Public' }); if (item.privateIp) result.push({ value: item.privateIp, type: 'ip', scope: 'private', info: 'Private' }); }); return result; } + get environment() { + return this.props.scEnvironment; + } + get keyName() { return this.props.keyName; } @@ -43,6 +57,14 @@ class ScEnvSshConnRowExpanded extends React.Component { return this.props.connectionId; } + get envsStore() { + return this.props.scEnvironmentsStore; + } + + getConnectionStore() { + return this.envsStore.getScEnvConnectionStore(this.environment.id); + } + componentDidMount() { this.startCountDown(); } @@ -77,6 +99,34 @@ class ScEnvSshConnRowExpanded extends React.Component { }, 1000); }; + handleConnect = () => + action(async () => { + try { + const connectionId = this.connectionId; + const store = this.getConnectionStore(); + let urlObj; + if (process.env.REACT_APP_IS_APP_STREAM_ENABLED) { + urlObj = await store.createConnectionUrl(connectionId); + } + const appStreamUrl = urlObj.url; + if (appStreamUrl) { + // We use noopener and noreferrer for good practices https://developer.mozilla.org/en-US/docs/Web/API/Window/open#noopener + openWindow(appStreamUrl, 'noopener,noreferrer'); + const newTab = openWindow('about:blank'); + newTab.location = appStreamUrl; + } else { + throw Error('AppStream URL was not returned by the API'); + } + this.processingId = connectionId; + } catch (error) { + displayError(error); + } finally { + runInAction(() => { + this.processingId = ''; + }); + } + }); + render() { const connectionId = this.connectionId; const keyName = this.keyName; @@ -88,7 +138,11 @@ class ScEnvSshConnRowExpanded extends React.Component { - {this.renderInfo()} + {process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true' ? ( + {this.renderAppStreamInfo()} + ) : ( + {this.renderInfo()} + )}
{this.renderCountDown()}
@@ -96,17 +150,79 @@ class ScEnvSshConnRowExpanded extends React.Component {
-
- Example: - - {example} - -
+ {process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true' ? ( + <> + ) : ( +
+ Example: + + {example} + +
+ )}
); } + renderAppStreamInfo() { + const interfaces = this.networkInterfaces; + const moreThanOne = _.size(interfaces) > 1; + const connectionId = this.connectionId; + const processingId = this.processingId; + const isDisabled = id => processingId !== id && !_.isEmpty(processingId); + const isLoading = id => processingId === id; + + return ( +
+ Connection instructions for your AppStream workspace: + + + The IP Address or DNS of the instance.{' '} + {moreThanOne ? 'Ask your administrator if you are not sure which one to use:' : ''} + + {_.map(interfaces, network => ( + + {this.renderHostLabel(network)} + + + ))} + + + + You downloaded the private key when you created the SSH key. Save this key's content into + AppStream's Notepad application in .PEM format + + Open PuttyGen in AppStream and convert your private PEM key to PPK format + + With the PPK file from above step and private IP address given by SWB to connect with their instance use + Putty to connect to the instance + + +
More information on connecting to your Linux instance from Windows OS:
+ + + Connecting from Windows via Putty + + + +
+ ); + } + renderInfo() { const interfaces = this.networkInterfaces; const moreThanOne = _.size(interfaces) > 1; @@ -187,14 +303,17 @@ class ScEnvSshConnRowExpanded extends React.Component { // see https://medium.com/@mweststrate/mobx-4-better-simpler-faster-smaller-c1fbc08008da decorate(ScEnvSshConnRowExpanded, { + envsStore: computed, + appStreamUrl: computed, networkInterfaces: computed, keyName: computed, connectionId: computed, intervalId: observable, countDown: observable, + processingId: observable, expired: observable, startCountDown: action, clearInterval: action, }); -export default inject()(withRouter(observer(ScEnvSshConnRowExpanded))); +export default inject('scEnvironmentsStore')(withRouter(observer(ScEnvSshConnRowExpanded))); diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js index 77e07257e5..8ac105eaa9 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js @@ -155,7 +155,7 @@ class ScEnvironmentHttpConnections extends React.Component { <> - Connection instructions for your {item.id} AppStream workspace: + Connection instructions for your AppStream workspace: Step 1: Click the Connect button to start an AppStream session diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js index 7ab15cf560..76b4d94901 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js @@ -131,56 +131,69 @@ class ScEnvironmentRdpConnectionRow extends React.Component { const moreThanOne = _.size(interfaces) > 1; return ( - - - - Your Windows workspace can be accessed via an RDP client by using the DNS host name and credentials defined - below. - - - - The IP Address or DNS of the instance.{' '} - {moreThanOne ? 'Ask your administrator if you are not sure which one to use:' : ''} - - {_.map(interfaces, network => ( - - {this.renderHostLabel(network)} - - - ))} - - - - The username and password: - - - {this.renderUsernameLabel(username)} - - - - {this.renderPasswordLabel(password)} - - + <> + + + + Your Windows workspace can be accessed via an RDP client by using the DNS host name and credentials + defined below. + + + {process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true' ? ( + <> + ) : ( + + The IP Address or DNS of the instance.{' '} + {moreThanOne ? 'Ask your administrator if you are not sure which one to use:' : ''} + + {_.map(interfaces, network => ( + + {this.renderHostLabel(network)} + + + ))} + - - - -
- Additional information about connecting via RDP can be found in the documentation below: -
- - - Connect to Your Windows Instance - - -
-
+ )} + + The username and password: + + + {this.renderUsernameLabel(username)} + + + + {this.renderPasswordLabel(password)} + + + + + +
+
+ Additional information about connecting via RDP can be found in the documentation below: +
+ + + Connect to Your Windows Instance + + +
+
+ + + + + + ); } diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js index 077a858ee7..360036e73a 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js @@ -86,8 +86,13 @@ class ScEnvironmentSshConnectionRow extends React.Component { this.processingSendKey = true; try { const result = await store.sendSshKey(connectionId, keyId); + let urlObj; + runInAction(() => { this.networkInterfaces = _.get(result, 'networkInterfaces'); + if (process.env.REACT_APP_IS_APP_STREAM_ENABLED) { + this.appStreamUrl = urlObj.url; + } }); } catch (error) { displayError(error); diff --git a/main/solution/backend/src/lambdas/workflow-loop-runner/plugins/plugin-registry.js b/main/solution/backend/src/lambdas/workflow-loop-runner/plugins/plugin-registry.js index 8e7e09ca37..8a76de6bed 100644 --- a/main/solution/backend/src/lambdas/workflow-loop-runner/plugins/plugin-registry.js +++ b/main/solution/backend/src/lambdas/workflow-loop-runner/plugins/plugin-registry.js @@ -30,6 +30,8 @@ const environmentScWfPlugin = require('@aws-ee/environment-sc-workflows/lib/plug const bassRaasEnvTypeVarsPlugin = require('@aws-ee/base-raas-services/lib/plugins/env-provisioning-plugin'); const rolesOnlyStrategyPlugin = require('@aws-ee/base-raas-services/lib/plugins/roles-only-strategy-plugin'); const legacyStrategyPlugin = require('@aws-ee/base-raas-services/lib/plugins/legacy-strategy-plugin'); +const baseRaasAppstreamServicesPlugin = require('@aws-ee/base-raas-appstream-rest-api/lib/plugins/services-plugin'); +const baseRaasAppstreamEnvTypeVarsPlugin = require('@aws-ee/base-raas-appstream-services/lib/plugins/env-sc-provisioning-plugin'); const servicesPlugin = require('services/lib/plugins/services-plugin'); @@ -38,6 +40,7 @@ const extensionPoints = { baseServicesPlugin, baseWfServicesPlugin, baseRaasServicesPlugin, + baseRaasAppstreamServicesPlugin, environmentTypeServicesPlugin, keyPairServicesPlugin, servicesPlugin, @@ -48,7 +51,7 @@ const extensionPoints = { 'workflows': [baseRaasWorkflowsPlugin, environmentScWfPlugin], 'workflow-assignments': [], 'cfn-templates': [baseRaasCfnTemplatesPlugin], - 'env-provisioning': [bassRaasEnvTypeVarsPlugin], // Plugins to participate in resolving list of "Environment Type Configuration Variables". See "addons/addon-environment-sc-api/README.md" to understand what "Environment Type Configuration Variables" are + 'env-provisioning': [bassRaasEnvTypeVarsPlugin, baseRaasAppstreamEnvTypeVarsPlugin], // Plugins to participate in resolving list of "Environment Type Configuration Variables". See "addons/addon-environment-sc-api/README.md" to understand what "Environment Type Configuration Variables" are // --- Authorization Plugins ---/ From 67b31261b25c14e9e8c6630f48712e722059aac7 Mon Sep 17 00:00:00 2001 From: SanketD92 Date: Wed, 14 Jul 2021 17:55:37 -0400 Subject: [PATCH 02/12] feat: adding ec2linux ps1 file --- .../plugins/env-sc-connection-url-plugin.js | 2 +- .../parts/ScEnvSshConnRowExpanded.js | 64 +------------------ .../parts/ScEnvironmentSshConnectionRow.js | 51 +++++++++++++++ scripts/app-stream/SETUP.md | 1 + scripts/app-stream/buildImage.ps1 | 2 + scripts/app-stream/ec2linux.ps1 | 3 + 6 files changed, 60 insertions(+), 63 deletions(-) create mode 100644 scripts/app-stream/ec2linux.ps1 diff --git a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/env-sc-connection-url-plugin.js b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/env-sc-connection-url-plugin.js index 9d2f985dcd..e99ab4139e 100644 --- a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/env-sc-connection-url-plugin.js +++ b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/env-sc-connection-url-plugin.js @@ -39,7 +39,7 @@ async function createConnectionUrl({ envId, connection }, { requestContext, cont }); appStreamUrl = await appStreamScService.getStreamingUrl(requestContext, { environmentId: envId, - applicationId: 'Notepad', + applicationId: 'EC2Linux', }); } else if (connection.scheme === 'rdp') { log.debug({ diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js index e0c320cd17..3c9111b15d 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js @@ -3,16 +3,10 @@ import React from 'react'; import { decorate, action, runInAction, observable, computed } from 'mobx'; import { observer, inject } from 'mobx-react'; import { withRouter } from 'react-router-dom'; -import { Table, List, Segment, Label, Grid, Button } from 'semantic-ui-react'; - -import { displayError } from '@aws-ee/base-ui/dist/helpers/notification'; +import { Table, List, Segment, Label, Grid } from 'semantic-ui-react'; import CopyToClipboard from '../../helpers/CopyToClipboard'; -const openWindow = (url, windowFeatures) => { - return window.open(url, '_blank', windowFeatures); -}; - // expected props // networkInterfaces (via props) // keyName (via props) @@ -27,7 +21,6 @@ class ScEnvSshConnRowExpanded extends React.Component { this.countDown = undefined; this.intervalId = undefined; this.expired = false; - this.processingId = ''; }); } @@ -57,14 +50,6 @@ class ScEnvSshConnRowExpanded extends React.Component { return this.props.connectionId; } - get envsStore() { - return this.props.scEnvironmentsStore; - } - - getConnectionStore() { - return this.envsStore.getScEnvConnectionStore(this.environment.id); - } - componentDidMount() { this.startCountDown(); } @@ -99,34 +84,6 @@ class ScEnvSshConnRowExpanded extends React.Component { }, 1000); }; - handleConnect = () => - action(async () => { - try { - const connectionId = this.connectionId; - const store = this.getConnectionStore(); - let urlObj; - if (process.env.REACT_APP_IS_APP_STREAM_ENABLED) { - urlObj = await store.createConnectionUrl(connectionId); - } - const appStreamUrl = urlObj.url; - if (appStreamUrl) { - // We use noopener and noreferrer for good practices https://developer.mozilla.org/en-US/docs/Web/API/Window/open#noopener - openWindow(appStreamUrl, 'noopener,noreferrer'); - const newTab = openWindow('about:blank'); - newTab.location = appStreamUrl; - } else { - throw Error('AppStream URL was not returned by the API'); - } - this.processingId = connectionId; - } catch (error) { - displayError(error); - } finally { - runInAction(() => { - this.processingId = ''; - }); - } - }); - render() { const connectionId = this.connectionId; const keyName = this.keyName; @@ -168,10 +125,6 @@ class ScEnvSshConnRowExpanded extends React.Component { renderAppStreamInfo() { const interfaces = this.networkInterfaces; const moreThanOne = _.size(interfaces) > 1; - const connectionId = this.connectionId; - const processingId = this.processingId; - const isDisabled = id => processingId !== id && !_.isEmpty(processingId); - const isLoading = id => processingId === id; return (
@@ -209,16 +162,6 @@ class ScEnvSshConnRowExpanded extends React.Component { Connecting from Windows via Putty -
); } @@ -303,17 +246,14 @@ class ScEnvSshConnRowExpanded extends React.Component { // see https://medium.com/@mweststrate/mobx-4-better-simpler-faster-smaller-c1fbc08008da decorate(ScEnvSshConnRowExpanded, { - envsStore: computed, - appStreamUrl: computed, networkInterfaces: computed, keyName: computed, connectionId: computed, intervalId: observable, countDown: observable, - processingId: observable, expired: observable, startCountDown: action, clearInterval: action, }); -export default inject('scEnvironmentsStore')(withRouter(observer(ScEnvSshConnRowExpanded))); +export default inject()(withRouter(observer(ScEnvSshConnRowExpanded))); diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js index 360036e73a..5c821415f9 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js @@ -9,6 +9,10 @@ import { displayError } from '@aws-ee/base-ui/dist/helpers/notification'; import ScEnvSshConnRowExpanded from './ScEnvSshConnRowExpanded'; +const openWindow = (url, windowFeatures) => { + return window.open(url, '_blank', windowFeatures); +}; + // expected props // - scEnvironment (via prop) // - connectionId (via prop) @@ -26,6 +30,7 @@ class ScEnvironmentSshConnectionRow extends React.Component { this.processingSendKey = false; // We default the selected key (if any) to the first latest active key this.selectedKeyId = key.id; + this.appStreamUrl = undefined; }); } @@ -87,6 +92,9 @@ class ScEnvironmentSshConnectionRow extends React.Component { try { const result = await store.sendSshKey(connectionId, keyId); let urlObj; + if (process.env.REACT_APP_IS_APP_STREAM_ENABLED) { + urlObj = await store.createConnectionUrl(connectionId); + } runInAction(() => { this.networkInterfaces = _.get(result, 'networkInterfaces'); @@ -103,6 +111,34 @@ class ScEnvironmentSshConnectionRow extends React.Component { } }; + handleConnect = () => + action(async () => { + try { + const connectionId = this.connectionId; + const store = this.getConnectionStore(); + let urlObj; + if (process.env.REACT_APP_IS_APP_STREAM_ENABLED) { + urlObj = await store.createConnectionUrl(connectionId); + } + const appStreamUrl = urlObj.url; + if (appStreamUrl) { + // We use noopener and noreferrer for good practices https://developer.mozilla.org/en-US/docs/Web/API/Window/open#noopener + openWindow(appStreamUrl, 'noopener,noreferrer'); + const newTab = openWindow('about:blank'); + newTab.location = appStreamUrl; + } else { + throw Error('AppStream URL was not returned by the API'); + } + this.processingId = connectionId; + } catch (error) { + displayError(error); + } finally { + runInAction(() => { + this.processingId = ''; + }); + } + }); + handleKeyChange = (e, data) => { const value = _.get(data, 'value'); const changed = value !== this.selectedKeyId; @@ -119,6 +155,7 @@ class ScEnvironmentSshConnectionRow extends React.Component { const options = this.keyPairOptions; const selectedKeyId = this.selectedKeyId; const selectedKeyName = this.selectedKeyName; + const rows = [ @@ -160,10 +197,23 @@ class ScEnvironmentSshConnectionRow extends React.Component { networkInterfaces={networkInterfaces} keyName={selectedKeyName} connectionId={item.id} + appStreamUrl={this.appStreamUrl} />, ); } + if (process.env.REACT_APP_IS_APP_STREAM_ENABLED) { + rows.push( + + + + + , + ); + } + return rows; } } @@ -181,6 +231,7 @@ decorate(ScEnvironmentSshConnectionRow, { selectedKeyId: observable, networkInterfaces: observable, processingSendKey: observable, + appStreamUrl: observable, handleActivate: action, handleKeyChange: action, }); diff --git a/scripts/app-stream/SETUP.md b/scripts/app-stream/SETUP.md index 9dd8c0b03d..d9abc38e25 100644 --- a/scripts/app-stream/SETUP.md +++ b/scripts/app-stream/SETUP.md @@ -30,6 +30,7 @@ cd ~\Documents # Pull the Image Builder script from Github Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/feat-secure-workspace-egress/scripts/app-stream/buildImage.ps1 -OutFile buildImage.ps1 +Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/feat-secure-workspace-egress/scripts/app-stream/ec2linux.ps1 -OutFile \`"C:\App\ec2linux.ps1\`" # Execute Image builder script .\buildImage.ps1 diff --git a/scripts/app-stream/buildImage.ps1 b/scripts/app-stream/buildImage.ps1 index 46acee88c6..c392166050 100644 --- a/scripts/app-stream/buildImage.ps1 +++ b/scripts/app-stream/buildImage.ps1 @@ -15,6 +15,8 @@ cd "C:\Program Files\Amazon\Photon\ConsoleImageBuilder" .\image-assistant.exe add-application --absolute-app-path "C:\Program Files (x86)\Mozilla Firefox\firefox.exe" --display-name Firefox --name Firefox +.\image-assistant.exe add-application --absolute-app-path "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" --display-name EC2Linux --name EC2Linux --launch-parameters " -file \`"C:\App\ec2linux.ps1\`" -ExecutionPolicy Bypass" + # Create App $ImageName="ServiceWorkbench_v1_" + $(Get-Date -Format "MM-dd-yyyy-hh-mm-ss") .\image-assistant.exe create-image --name $ImageName diff --git a/scripts/app-stream/ec2linux.ps1 b/scripts/app-stream/ec2linux.ps1 new file mode 100644 index 0000000000..c0ee86766c --- /dev/null +++ b/scripts/app-stream/ec2linux.ps1 @@ -0,0 +1,3 @@ +Start-Process -FilePath "C:\Program Files\PuTTY\putty.exe" +Start-Process -FilePath "C:\Program Files\PuTTY\puttygen.exe" +Start-Process -FilePath "C:\Windows\System32\notepad.exe" \ No newline at end of file From f2ee60168514222c40e7a60e94b29c0c7a376c48 Mon Sep 17 00:00:00 2001 From: SanketD92 Date: Wed, 14 Jul 2021 18:02:59 -0400 Subject: [PATCH 03/12] fix: add ec2linux in buildImage --- scripts/app-stream/SETUP.md | 3 +-- scripts/app-stream/buildImage.ps1 | 10 +++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/scripts/app-stream/SETUP.md b/scripts/app-stream/SETUP.md index d9abc38e25..4a3fb4eedd 100644 --- a/scripts/app-stream/SETUP.md +++ b/scripts/app-stream/SETUP.md @@ -29,8 +29,7 @@ Note: Please set up your [AWS Profile](https://docs.aws.amazon.com/cli/latest/us cd ~\Documents # Pull the Image Builder script from Github -Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/feat-secure-workspace-egress/scripts/app-stream/buildImage.ps1 -OutFile buildImage.ps1 -Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/feat-secure-workspace-egress/scripts/app-stream/ec2linux.ps1 -OutFile \`"C:\App\ec2linux.ps1\`" +Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/feat-appstream-ui/scripts/app-stream/buildImage.ps1 -OutFile buildImage.ps1 # Execute Image builder script .\buildImage.ps1 diff --git a/scripts/app-stream/buildImage.ps1 b/scripts/app-stream/buildImage.ps1 index c392166050..3730eb64b8 100644 --- a/scripts/app-stream/buildImage.ps1 +++ b/scripts/app-stream/buildImage.ps1 @@ -3,6 +3,14 @@ Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManage choco install -y putty.install +$customFirefoxLauncherPath = "C:\App" +If(!(test-path $customFirefoxLauncherPath)) +{ + New-Item -ItemType Directory -Force -Path $customFirefoxLauncherPath +} + +Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/feat-appstream-ui/scripts/app-stream/ec2linux.ps1 -OutFile 'C:\App\ec2linux.ps1' + # Add Applications cd "C:\Program Files\Amazon\Photon\ConsoleImageBuilder" @@ -15,7 +23,7 @@ cd "C:\Program Files\Amazon\Photon\ConsoleImageBuilder" .\image-assistant.exe add-application --absolute-app-path "C:\Program Files (x86)\Mozilla Firefox\firefox.exe" --display-name Firefox --name Firefox -.\image-assistant.exe add-application --absolute-app-path "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" --display-name EC2Linux --name EC2Linux --launch-parameters " -file \`"C:\App\ec2linux.ps1\`" -ExecutionPolicy Bypass" +.\image-assistant.exe add-application --absolute-app-path "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" --display-name EC2Linux --name EC2Linux --launch-parameters " -file \`"C:\Windows\System32\ec2linux.ps1\`" -ExecutionPolicy Bypass" # Create App $ImageName="ServiceWorkbench_v1_" + $(Get-Date -Format "MM-dd-yyyy-hh-mm-ss") From 2823b38b92750d2b412f09dba9302ef7dbf5ba53 Mon Sep 17 00:00:00 2001 From: SanketD92 Date: Wed, 14 Jul 2021 18:08:06 -0400 Subject: [PATCH 04/12] feat: update ec2linux ps1 location --- scripts/app-stream/buildImage.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/app-stream/buildImage.ps1 b/scripts/app-stream/buildImage.ps1 index 3730eb64b8..208eb8dddf 100644 --- a/scripts/app-stream/buildImage.ps1 +++ b/scripts/app-stream/buildImage.ps1 @@ -23,7 +23,7 @@ cd "C:\Program Files\Amazon\Photon\ConsoleImageBuilder" .\image-assistant.exe add-application --absolute-app-path "C:\Program Files (x86)\Mozilla Firefox\firefox.exe" --display-name Firefox --name Firefox -.\image-assistant.exe add-application --absolute-app-path "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" --display-name EC2Linux --name EC2Linux --launch-parameters " -file \`"C:\Windows\System32\ec2linux.ps1\`" -ExecutionPolicy Bypass" +.\image-assistant.exe add-application --absolute-app-path "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" --display-name EC2Linux --name EC2Linux --launch-parameters " -file \`"C:\App\ec2linux.ps1\`" -ExecutionPolicy Bypass" # Create App $ImageName="ServiceWorkbench_v1_" + $(Get-Date -Format "MM-dd-yyyy-hh-mm-ss") From 4479cc0b3149f3612445a9be5043aba240498203 Mon Sep 17 00:00:00 2001 From: SanketD92 Date: Wed, 14 Jul 2021 22:13:39 -0400 Subject: [PATCH 05/12] feat: UI improvements appstream --- .../parts/ScEnvHttpConnectionExpanded.js | 141 ------------- .../parts/ScEnvSshConnRowExpanded.js | 11 +- .../parts/ScEnvironmentHttpConnections.js | 193 +++++++++++++----- .../parts/ScEnvironmentSshConnectionRow.js | 28 ++- scripts/app-stream/buildImage.ps1 | 4 +- 5 files changed, 168 insertions(+), 209 deletions(-) delete mode 100644 addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvHttpConnectionExpanded.js diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvHttpConnectionExpanded.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvHttpConnectionExpanded.js deleted file mode 100644 index 72b91bd934..0000000000 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvHttpConnectionExpanded.js +++ /dev/null @@ -1,141 +0,0 @@ -import _ from 'lodash'; -import React from 'react'; -import { decorate, action, runInAction, observable, computed } from 'mobx'; -import { observer, inject } from 'mobx-react'; -import { withRouter } from 'react-router-dom'; -import { Table, Segment, Grid } from 'semantic-ui-react'; - -import CopyToClipboard from '../../helpers/CopyToClipboard'; - -// expected props -// destinationUrl (via props) -// keyName (via props) -// connectionId (via props) -// timeout (via props) -class ScEnvHttpConnectionExpanded extends React.Component { - constructor(props) { - super(props); - runInAction(() => { - // The count down value - this.countDown = undefined; - this.intervalId = undefined; - this.expired = false; - }); - } - - get destinationUrl() { - return this.props.destinationUrl; - } - - get timeout() { - return this.props.timeout; - } - - get keyName() { - return this.props.keyName; - } - - get connectionId() { - return this.props.connectionId; - } - - componentDidMount() { - this.startCountDown(); - } - - componentWillUnmount() { - this.clearInterval(); - } - - clearInterval() { - if (!_.isUndefined(this.intervalId)) { - clearInterval(this.intervalId); - this.intervalId = undefined; - } - this.countDown = undefined; - this.expired = false; - } - - startCountDown = () => { - if (!_.isUndefined(this.intervalId)) return; - this.countDown = this.timeout; - - this.intervalId = setInterval(async () => { - // eslint-disable-next-line consistent-return - runInAction(() => { - if (this.countDown <= 0) { - this.clearInterval(); - this.expired = true; - return; - } - this.countDown -= 1; - }); - }, 1000); - }; - - render() { - const connectionId = this.connectionId; - - return ( - - - - - {this.renderInfo()} - - -
{this.renderCountDown()}
-
-
-
-
-
-
- ); - } - - renderInfo() { - const destinationUrl = this.destinationUrl; - - return ( - <> -
- Click on this icon to copy the workspace destination URL: - -
-
(You can come back to this page to copy the destination URL)
- - ); - } - - renderCountDown() { - const countDown = this.countDown; - const expired = this.expired; - - if (expired) { - return
AppStream session has been opened in a new tab
; - } - - return ( -
-
- Secure AppStream tab opens in
{countDown}
seconds -
-
- ); - } -} - -// see https://medium.com/@mweststrate/mobx-4-better-simpler-faster-smaller-c1fbc08008da -decorate(ScEnvHttpConnectionExpanded, { - destinationUrl: computed, - keyName: computed, - connectionId: computed, - intervalId: observable, - countDown: observable, - expired: observable, - startCountDown: action, - clearInterval: action, -}); - -export default inject()(withRouter(observer(ScEnvHttpConnectionExpanded))); diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js index 3c9111b15d..7c7f044864 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js @@ -16,6 +16,7 @@ import CopyToClipboard from '../../helpers/CopyToClipboard'; class ScEnvSshConnRowExpanded extends React.Component { constructor(props) { super(props); + runInAction(() => { // The count down value this.countDown = undefined; @@ -24,6 +25,10 @@ class ScEnvSshConnRowExpanded extends React.Component { }); } + get isAppStreamEnabled() { + return process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true'; + } + get networkInterfaces() { const entries = this.props.networkInterfaces; if (_.isEmpty(entries)) return []; @@ -95,19 +100,19 @@ class ScEnvSshConnRowExpanded extends React.Component { - {process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true' ? ( + {this.isAppStreamEnabled ? ( {this.renderAppStreamInfo()} ) : ( {this.renderInfo()} )} -
{this.renderCountDown()}
+
{this.renderCountDown()}
- {process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true' ? ( + {this.isAppStreamEnabled ? ( <> ) : (
diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js index 8ac105eaa9..415a83f865 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js @@ -3,11 +3,11 @@ import React from 'react'; import { decorate, computed, action, runInAction, observable } from 'mobx'; import { observer, inject } from 'mobx-react'; import { withRouter } from 'react-router-dom'; -import { Segment, Icon, Button, Header, Table, List } from 'semantic-ui-react'; +import { Segment, Icon, Button, Header, Grid, Table, List } from 'semantic-ui-react'; import { displayError } from '@aws-ee/base-ui/dist/helpers/notification'; -import ScEnvHttpConnectionExpanded from './ScEnvHttpConnectionExpanded'; +import CopyToClipboard from '../../helpers/CopyToClipboard'; const openWindow = (url, windowFeatures) => { return window.open(url, '_blank', windowFeatures); @@ -22,7 +22,9 @@ class ScEnvironmentHttpConnections extends React.Component { runInAction(() => { // The id of the connection that is being processed this.processingId = ''; + this.appStreamGeneratingId = ''; this.destinationUrl = undefined; + this.streamingUrl = undefined; this.timeout = 10; }); } @@ -55,7 +57,10 @@ class ScEnvironmentHttpConnections extends React.Component { const connectInfo = _.find(connections, ['id', id]) || {}; let url = connectInfo.url; - this.processingId = id; + runInAction(() => { + this.processingId = id; + }); + try { if (url) { // We use noopener and noreferrer for good practices https://developer.mozilla.org/en-US/docs/Web/API/Window/open#noopener @@ -64,17 +69,7 @@ class ScEnvironmentHttpConnections extends React.Component { const urlObj = await store.createConnectionUrl(id); url = urlObj.url; - // If AppStream is enabled, copy destination URL to clipboard before new tab loads - if (process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true') { - runInAction(() => { - this.destinationUrl = urlObj.appstreamDestinationUrl; - }); - // Allow users time to copy the destination URL - setTimeout(() => { - const newTab = openWindow('about:blank'); - newTab.location = url; - }, this.timeout * 1000); - } else { + if (url) { const newTab = openWindow('about:blank'); newTab.location = url; } @@ -88,15 +83,51 @@ class ScEnvironmentHttpConnections extends React.Component { } }); + handleAppStreamConnect = (url, id) => + action(async () => { + try { + runInAction(() => { + this.appStreamConnectingId = id; + }); + if (url) { + const newTab = openWindow('about:blank'); + newTab.location = url; + } + } catch (error) { + displayError(error); + } finally { + runInAction(() => { + this.appStreamConnectingId = ''; + }); + } + }); + + handleGenerateAppStreamUrl = id => + action(async () => { + const store = this.getConnectionStore(); + runInAction(() => { + this.appStreamGeneratingId = id; + }); + try { + const urlObj = await store.createConnectionUrl(id); + runInAction(() => { + this.destinationUrl = urlObj.appstreamDestinationUrl; + this.streamingUrl = urlObj.url; + }); + } catch (error) { + displayError(error); + } finally { + runInAction(() => { + this.appStreamGeneratingId = ''; + }); + } + }); + render() { const env = this.environment; const state = env.state; const canConnect = state.canConnect; const connections = this.connections; - const destinationUrl = this.destinationUrl; - const processingId = this.processingId; - const isDisabled = id => processingId !== id && !_.isEmpty(processingId); - const isLoading = id => processingId === id; if (!canConnect) return null; return ( @@ -107,46 +138,112 @@ class ScEnvironmentHttpConnections extends React.Component { HTTP Connections - - {_.map(connections, item => ( + {this.renderBody(connections)} + +
+ ); + } + + renderAppStreamBody(connections) { + const processingId = this.processingId; + const streamingUrl = this.streamingUrl; + const destinationUrl = this.destinationUrl; + const isDisabled = id => processingId !== id && !_.isEmpty(processingId); + const isLoading = id => processingId === id; + + return ( + <> + {_.map(connections, item => ( + <> + {this.renderAppstreamInstructions(item)} + + + + +
{item.name || 'Generate'}
+
+
+ + {destinationUrl ? ( <> - {process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true' ? ( - this.renderAppstreamInstructions(item) - ) : ( - <> - )} - - + + + + + +
+ Click on this icon to copy the workspace destination URL: + +
+
+
+
+
+
+ + + - -
{item.name || 'Connect'}
- {destinationUrl ? ( - - ) : ( - <> - )} - ))} - - - + ) : ( + <> + )} + + ))} + + ); + } + + renderBody(connections) { + if (process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true') { + return this.renderAppStreamBody(connections); + } + + const processingId = this.processingId; + + const isDisabled = id => processingId !== id && !_.isEmpty(processingId); + const isLoading = id => processingId === id; + return ( + <> + {_.map(connections, item => ( + <> + + + + +
{item.name || 'Connect'}
+
+
+ + ))} + ); } @@ -192,7 +289,9 @@ decorate(ScEnvironmentHttpConnections, { environment: computed, connections: computed, processingId: observable, + appStreamGeneratingId: observable, destinationUrl: observable, + streamingUrl: observable, }); export default inject('scEnvironmentsStore')(withRouter(observer(ScEnvironmentHttpConnections))); diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js index 5c821415f9..a8fd3ef789 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js @@ -3,7 +3,7 @@ import React from 'react'; import { decorate, computed, action, runInAction, observable } from 'mobx'; import { observer, inject } from 'mobx-react'; import { withRouter } from 'react-router-dom'; -import { Button, Table, Dropdown } from 'semantic-ui-react'; +import { Button, Table, Dropdown, Grid } from 'semantic-ui-react'; import { displayError } from '@aws-ee/base-ui/dist/helpers/notification'; @@ -34,6 +34,10 @@ class ScEnvironmentSshConnectionRow extends React.Component { }); } + get isAppStreamEnabled() { + return process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true'; + } + get environment() { return this.props.scEnvironment; } @@ -91,16 +95,9 @@ class ScEnvironmentSshConnectionRow extends React.Component { this.processingSendKey = true; try { const result = await store.sendSshKey(connectionId, keyId); - let urlObj; - if (process.env.REACT_APP_IS_APP_STREAM_ENABLED) { - urlObj = await store.createConnectionUrl(connectionId); - } runInAction(() => { this.networkInterfaces = _.get(result, 'networkInterfaces'); - if (process.env.REACT_APP_IS_APP_STREAM_ENABLED) { - this.appStreamUrl = urlObj.url; - } }); } catch (error) { displayError(error); @@ -116,10 +113,7 @@ class ScEnvironmentSshConnectionRow extends React.Component { try { const connectionId = this.connectionId; const store = this.getConnectionStore(); - let urlObj; - if (process.env.REACT_APP_IS_APP_STREAM_ENABLED) { - urlObj = await store.createConnectionUrl(connectionId); - } + const urlObj = await store.createConnectionUrl(connectionId); const appStreamUrl = urlObj.url; if (appStreamUrl) { // We use noopener and noreferrer for good practices https://developer.mozilla.org/en-US/docs/Web/API/Window/open#noopener @@ -202,13 +196,15 @@ class ScEnvironmentSshConnectionRow extends React.Component { ); } - if (process.env.REACT_APP_IS_APP_STREAM_ENABLED) { + if (this.isAppStreamEnabled && !_.isUndefined(networkInterfaces)) { rows.push( - + + + , ); diff --git a/scripts/app-stream/buildImage.ps1 b/scripts/app-stream/buildImage.ps1 index 208eb8dddf..1a6d0004da 100644 --- a/scripts/app-stream/buildImage.ps1 +++ b/scripts/app-stream/buildImage.ps1 @@ -9,7 +9,7 @@ If(!(test-path $customFirefoxLauncherPath)) New-Item -ItemType Directory -Force -Path $customFirefoxLauncherPath } -Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/feat-appstream-ui/scripts/app-stream/ec2linux.ps1 -OutFile 'C:\App\ec2linux.ps1' +Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/feat-appstream-ui/scripts/app-stream/ec2linux.ps1 -OutFile 'C:\Users\Public\Documents\EC2Linux.ps1' # Add Applications @@ -23,7 +23,7 @@ cd "C:\Program Files\Amazon\Photon\ConsoleImageBuilder" .\image-assistant.exe add-application --absolute-app-path "C:\Program Files (x86)\Mozilla Firefox\firefox.exe" --display-name Firefox --name Firefox -.\image-assistant.exe add-application --absolute-app-path "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" --display-name EC2Linux --name EC2Linux --launch-parameters " -file \`"C:\App\ec2linux.ps1\`" -ExecutionPolicy Bypass" +.\image-assistant.exe add-application --absolute-app-path "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" --display-name EC2Linux --name EC2Linux --launch-parameters '`"-File "C:\Users\Public\Documents\EC2Linux.ps1" -ExecutionPolicy Bypass`"' # Create App $ImageName="ServiceWorkbench_v1_" + $(Get-Date -Format "MM-dd-yyyy-hh-mm-ss") From c109389ceaac53c4e235b4506c5fb064f119155f Mon Sep 17 00:00:00 2001 From: SanketD92 Date: Wed, 14 Jul 2021 22:15:37 -0400 Subject: [PATCH 06/12] testing changes --- scripts/app-stream/SETUP.md | 2 +- scripts/app-stream/buildImage.ps1 | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/scripts/app-stream/SETUP.md b/scripts/app-stream/SETUP.md index 4a3fb4eedd..c221c0c771 100644 --- a/scripts/app-stream/SETUP.md +++ b/scripts/app-stream/SETUP.md @@ -29,7 +29,7 @@ Note: Please set up your [AWS Profile](https://docs.aws.amazon.com/cli/latest/us cd ~\Documents # Pull the Image Builder script from Github -Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/feat-appstream-ui/scripts/app-stream/buildImage.ps1 -OutFile buildImage.ps1 +Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/appstream-connection-updates/scripts/app-stream/buildImage.ps1 -OutFile buildImage.ps1 # Execute Image builder script .\buildImage.ps1 diff --git a/scripts/app-stream/buildImage.ps1 b/scripts/app-stream/buildImage.ps1 index 1a6d0004da..e0b99641dd 100644 --- a/scripts/app-stream/buildImage.ps1 +++ b/scripts/app-stream/buildImage.ps1 @@ -3,13 +3,7 @@ Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManage choco install -y putty.install -$customFirefoxLauncherPath = "C:\App" -If(!(test-path $customFirefoxLauncherPath)) -{ - New-Item -ItemType Directory -Force -Path $customFirefoxLauncherPath -} - -Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/feat-appstream-ui/scripts/app-stream/ec2linux.ps1 -OutFile 'C:\Users\Public\Documents\EC2Linux.ps1' +Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/appstream-connection-updates/scripts/app-stream/ec2linux.ps1 -OutFile 'C:\Users\Public\Documents\EC2Linux.ps1' # Add Applications From aa944efad0eca4001690a55e15b39cf2d95db107 Mon Sep 17 00:00:00 2001 From: SanketD92 Date: Wed, 14 Jul 2021 23:32:05 -0400 Subject: [PATCH 07/12] feat: adding UI changes for appstream --- .../parts/ScEnvSshConnRowExpanded.js | 15 ++--- .../parts/ScEnvironmentHttpConnections.js | 32 +++++----- .../parts/ScEnvironmentRdpConnectionRow.js | 59 ++++++++++++++++--- .../parts/ScEnvironmentSshConnectionRow.js | 13 ++-- .../parts/ScEnvironmentSshConnections.js | 4 +- scripts/app-stream/SETUP.md | 2 +- scripts/app-stream/buildImage.ps1 | 2 +- 7 files changed, 80 insertions(+), 47 deletions(-) diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js index 7c7f044864..554ef346b5 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js @@ -11,7 +11,6 @@ import CopyToClipboard from '../../helpers/CopyToClipboard'; // networkInterfaces (via props) // keyName (via props) // connectionId (via props) -// appStreamUrl (via props) // environment (via props) class ScEnvSshConnRowExpanded extends React.Component { constructor(props) { @@ -35,7 +34,7 @@ class ScEnvSshConnRowExpanded extends React.Component { const result = []; _.forEach(entries, item => { - if (item.publicDnsName && process.env.REACT_APP_IS_APP_STREAM_ENABLED !== 'true') + if (item.publicDnsName && !this.isAppStreamEnabled) result.push({ value: item.publicDnsName, type: 'dns', scope: 'public', info: 'Public' }); if (item.privateIp) result.push({ value: item.privateIp, type: 'ip', scope: 'private', info: 'Private' }); }); @@ -147,15 +146,11 @@ class ScEnvSshConnRowExpanded extends React.Component { ))} - - You downloaded the private key when you created the SSH key. Save this key's content into - AppStream's Notepad application in .PEM format - + Click the 'Connect' button below to navigate to the AppStream instance + You downloaded the private key when you created the SSH key. + Save this key's content into AppStream's Notepad application in .PEM format Open PuttyGen in AppStream and convert your private PEM key to PPK format - - With the PPK file from above step and private IP address given by SWB to connect with their instance use - Putty to connect to the instance - + Enter the PPK file and private IP address details in Putty to connect to EC2
More information on connecting to your Linux instance from Windows OS:
diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js index 415a83f865..ed68fad139 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js @@ -29,6 +29,10 @@ class ScEnvironmentHttpConnections extends React.Component { }); } + get isAppStreamEnabled() { + return process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true'; + } + get environment() { return this.props.scEnvironment; } @@ -134,7 +138,7 @@ class ScEnvironmentHttpConnections extends React.Component {
- + HTTP Connections @@ -145,11 +149,11 @@ class ScEnvironmentHttpConnections extends React.Component { } renderAppStreamBody(connections) { - const processingId = this.processingId; + const appStreamGeneratingId = this.appStreamGeneratingId; const streamingUrl = this.streamingUrl; const destinationUrl = this.destinationUrl; - const isDisabled = id => processingId !== id && !_.isEmpty(processingId); - const isLoading = id => processingId === id; + const isDisabled = id => appStreamGeneratingId !== id && !_.isEmpty(appStreamGeneratingId); + const isLoading = id => appStreamGeneratingId === id; return ( <> @@ -177,7 +181,7 @@ class ScEnvironmentHttpConnections extends React.Component { <> - +
@@ -190,7 +194,7 @@ class ScEnvironmentHttpConnections extends React.Component { - + - - + + {this.isAppStreamEnabled && windowsRdpInfo ? ( + + + + + + ) : ( + <> + )} ); } diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js index a8fd3ef789..979ebd7ac9 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js @@ -3,7 +3,7 @@ import React from 'react'; import { decorate, computed, action, runInAction, observable } from 'mobx'; import { observer, inject } from 'mobx-react'; import { withRouter } from 'react-router-dom'; -import { Button, Table, Dropdown, Grid } from 'semantic-ui-react'; +import { Button, Table, Dropdown } from 'semantic-ui-react'; import { displayError } from '@aws-ee/base-ui/dist/helpers/notification'; @@ -191,7 +191,6 @@ class ScEnvironmentSshConnectionRow extends React.Component { networkInterfaces={networkInterfaces} keyName={selectedKeyName} connectionId={item.id} - appStreamUrl={this.appStreamUrl} />, ); } @@ -199,12 +198,10 @@ class ScEnvironmentSshConnectionRow extends React.Component { if (this.isAppStreamEnabled && !_.isUndefined(networkInterfaces)) { rows.push( - - - - + + , ); diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnections.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnections.js index fb5dd5f00e..4b98408711 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnections.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnections.js @@ -114,7 +114,9 @@ class ScEnvironmentSshConnections extends React.Component { {_.map(connections, item => ( - + <> + + ))}
diff --git a/scripts/app-stream/SETUP.md b/scripts/app-stream/SETUP.md index c221c0c771..4a3fb4eedd 100644 --- a/scripts/app-stream/SETUP.md +++ b/scripts/app-stream/SETUP.md @@ -29,7 +29,7 @@ Note: Please set up your [AWS Profile](https://docs.aws.amazon.com/cli/latest/us cd ~\Documents # Pull the Image Builder script from Github -Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/appstream-connection-updates/scripts/app-stream/buildImage.ps1 -OutFile buildImage.ps1 +Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/feat-appstream-ui/scripts/app-stream/buildImage.ps1 -OutFile buildImage.ps1 # Execute Image builder script .\buildImage.ps1 diff --git a/scripts/app-stream/buildImage.ps1 b/scripts/app-stream/buildImage.ps1 index e0b99641dd..5ef54454d7 100644 --- a/scripts/app-stream/buildImage.ps1 +++ b/scripts/app-stream/buildImage.ps1 @@ -3,7 +3,7 @@ Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManage choco install -y putty.install -Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/appstream-connection-updates/scripts/app-stream/ec2linux.ps1 -OutFile 'C:\Users\Public\Documents\EC2Linux.ps1' +Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/feat-appstream-ui/scripts/app-stream/ec2linux.ps1 -OutFile 'C:\Users\Public\Documents\EC2Linux.ps1' # Add Applications From 8128d707e6b85840b9eb64a7e77ee31eeaee6619 Mon Sep 17 00:00:00 2001 From: SanketD92 Date: Thu, 15 Jul 2021 12:44:20 -0400 Subject: [PATCH 08/12] feat: changes per review --- .../lib/appstream/appstream-sc-service.js | 4 +- .../parts/ScEnvSshConnRowExpanded.js | 96 +++++++++++++------ .../parts/ScEnvironmentHttpConnections.js | 5 +- .../parts/ScEnvironmentRdpConnectionRow.js | 16 ++-- .../parts/ScEnvironmentSshConnectionRow.js | 49 +--------- .../parts/ScEnvironmentSshConnections.js | 33 ++++++- scripts/app-stream/SETUP.md | 2 +- scripts/app-stream/buildImage.ps1 | 2 +- 8 files changed, 110 insertions(+), 97 deletions(-) diff --git a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/appstream/appstream-sc-service.js b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/appstream/appstream-sc-service.js index 6be7ecb051..a2243b344f 100644 --- a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/appstream/appstream-sc-service.js +++ b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/appstream/appstream-sc-service.js @@ -118,7 +118,7 @@ class AppStreamScService extends Service { async getStreamingUrl(requestContext, { environmentId, applicationId }) { const isAppStreamEnabled = this.settings.get(settingKeys.isAppStreamEnabled); - if (!isAppStreamEnabled) return undefined; + if (isAppStreamEnabled !== 'true') return undefined; const environmentScService = await this.service('environmentScService'); @@ -152,7 +152,7 @@ class AppStreamScService extends Service { async urlForRemoteDesktop(requestContext, { environmentId, instanceId }) { const isAppStreamEnabled = this.settings.get(settingKeys.isAppStreamEnabled); - if (!isAppStreamEnabled) return undefined; + if (isAppStreamEnabled !== 'true') return undefined; const [environmentScKeypairService, environmentScService] = await this.service([ 'environmentScKeypairService', diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js index 554ef346b5..f96c8f477c 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js @@ -3,10 +3,16 @@ import React from 'react'; import { decorate, action, runInAction, observable, computed } from 'mobx'; import { observer, inject } from 'mobx-react'; import { withRouter } from 'react-router-dom'; -import { Table, List, Segment, Label, Grid } from 'semantic-ui-react'; +import { Table, List, Segment, Label, Grid, Button } from 'semantic-ui-react'; + +import { displayError } from '@aws-ee/base-ui/dist/helpers/notification'; import CopyToClipboard from '../../helpers/CopyToClipboard'; +const openWindow = (url, windowFeatures) => { + return window.open(url, '_blank', windowFeatures); +}; + // expected props // networkInterfaces (via props) // keyName (via props) @@ -21,6 +27,7 @@ class ScEnvSshConnRowExpanded extends React.Component { this.countDown = undefined; this.intervalId = undefined; this.expired = false; + this.processingId = undefined; }); } @@ -50,10 +57,18 @@ class ScEnvSshConnRowExpanded extends React.Component { return this.props.keyName; } + get envsStore() { + return this.props.scEnvironmentsStore; + } + get connectionId() { return this.props.connectionId; } + getConnectionStore() { + return this.envsStore.getScEnvConnectionStore(this.environment.id); + } + componentDidMount() { this.startCountDown(); } @@ -71,6 +86,31 @@ class ScEnvSshConnRowExpanded extends React.Component { this.expired = false; } + handleConnect = id => + action(async () => { + try { + runInAction(() => { + this.processingId = id; + }); + const store = this.getConnectionStore(); + const urlObj = await store.createConnectionUrl(id); + const appStreamUrl = urlObj.url; + if (appStreamUrl) { + const newTab = openWindow('about:blank'); + newTab.location = appStreamUrl; + } else { + throw Error('AppStream URL was not returned by the API'); + } + this.processingId = id; + } catch (error) { + displayError(error); + } finally { + runInAction(() => { + this.processingId = undefined; + }); + } + }); + startCountDown = () => { if (!_.isUndefined(this.intervalId)) return; this.countDown = 60; @@ -111,9 +151,7 @@ class ScEnvSshConnRowExpanded extends React.Component { - {this.isAppStreamEnabled ? ( - <> - ) : ( + {!this.isAppStreamEnabled && (
Example: @@ -128,41 +166,35 @@ class ScEnvSshConnRowExpanded extends React.Component { renderAppStreamInfo() { const interfaces = this.networkInterfaces; - const moreThanOne = _.size(interfaces) > 1; + const network = interfaces[0]; + const connectionId = this.connectionId; return ( -
- Connection instructions for your AppStream workspace: + - The IP Address or DNS of the instance.{' '} - {moreThanOne ? 'Ask your administrator if you are not sure which one to use:' : ''} - - {_.map(interfaces, network => ( - - {this.renderHostLabel(network)} - - - ))} + The Private IP Address to be used:{' '} + + + {this.renderHostLabel(network)} + + - Click the 'Connect' button below to navigate to the AppStream instance - You downloaded the private key when you created the SSH key. - Save this key's content into AppStream's Notepad application in .PEM format - Open PuttyGen in AppStream and convert your private PEM key to PPK format - Enter the PPK file and private IP address details in Putty to connect to EC2 -
More information on connecting to your Linux instance from Windows OS:
- - - Connecting from Windows via Putty - - -
+ Connect + + )} +
); } @@ -248,10 +280,12 @@ class ScEnvSshConnRowExpanded extends React.Component { decorate(ScEnvSshConnRowExpanded, { networkInterfaces: computed, keyName: computed, + envsStore: computed, connectionId: computed, intervalId: observable, countDown: observable, expired: observable, + processingId: observable, startCountDown: action, clearInterval: action, }); diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js index ed68fad139..1798cd3667 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js @@ -177,7 +177,7 @@ class ScEnvironmentHttpConnections extends React.Component { - {destinationUrl ? ( + {destinationUrl && ( <> @@ -207,8 +207,6 @@ class ScEnvironmentHttpConnections extends React.Component { - ) : ( - <> )} ))} @@ -222,7 +220,6 @@ class ScEnvironmentHttpConnections extends React.Component { } const processingId = this.processingId; - const isDisabled = id => processingId !== id && !_.isEmpty(processingId); const isLoading = id => processingId === id; return ( diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js index d9800a1c02..ddfc3198cf 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js @@ -79,22 +79,19 @@ class ScEnvironmentRdpConnectionRow extends React.Component { return result; } - handleConnect = () => + handleConnect = id => action(async () => { try { - const connectionId = this.connectionId; const store = this.getConnectionStore(); - const urlObj = await store.createConnectionUrl(connectionId); + const urlObj = await store.createConnectionUrl(id); const appStreamUrl = urlObj.url; if (appStreamUrl) { - // We use noopener and noreferrer for good practices https://developer.mozilla.org/en-US/docs/Web/API/Window/open#noopener - openWindow(appStreamUrl, 'noopener,noreferrer'); const newTab = openWindow('about:blank'); newTab.location = appStreamUrl; } else { throw Error('AppStream URL was not returned by the API'); } - this.processingId = connectionId; + this.processingId = id; } catch (error) { displayError(error); } finally { @@ -161,6 +158,7 @@ class ScEnvironmentRdpConnectionRow extends React.Component { const username = 'Administrator'; const password = windowsRdpInfo.password; const showPassword = this.showPassword; + const connectionId = this.connectionId; const moreThanOne = _.size(interfaces) > 1; return ( @@ -223,16 +221,14 @@ class ScEnvironmentRdpConnectionRow extends React.Component { - {this.isAppStreamEnabled && windowsRdpInfo ? ( + {this.isAppStreamEnabled && windowsRdpInfo && ( - - ) : ( - <> )} ); diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js index 979ebd7ac9..59dc5ecade 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js @@ -9,10 +9,6 @@ import { displayError } from '@aws-ee/base-ui/dist/helpers/notification'; import ScEnvSshConnRowExpanded from './ScEnvSshConnRowExpanded'; -const openWindow = (url, windowFeatures) => { - return window.open(url, '_blank', windowFeatures); -}; - // expected props // - scEnvironment (via prop) // - connectionId (via prop) @@ -30,14 +26,9 @@ class ScEnvironmentSshConnectionRow extends React.Component { this.processingSendKey = false; // We default the selected key (if any) to the first latest active key this.selectedKeyId = key.id; - this.appStreamUrl = undefined; }); } - get isAppStreamEnabled() { - return process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true'; - } - get environment() { return this.props.scEnvironment; } @@ -108,31 +99,6 @@ class ScEnvironmentSshConnectionRow extends React.Component { } }; - handleConnect = () => - action(async () => { - try { - const connectionId = this.connectionId; - const store = this.getConnectionStore(); - const urlObj = await store.createConnectionUrl(connectionId); - const appStreamUrl = urlObj.url; - if (appStreamUrl) { - // We use noopener and noreferrer for good practices https://developer.mozilla.org/en-US/docs/Web/API/Window/open#noopener - openWindow(appStreamUrl, 'noopener,noreferrer'); - const newTab = openWindow('about:blank'); - newTab.location = appStreamUrl; - } else { - throw Error('AppStream URL was not returned by the API'); - } - this.processingId = connectionId; - } catch (error) { - displayError(error); - } finally { - runInAction(() => { - this.processingId = ''; - }); - } - }); - handleKeyChange = (e, data) => { const value = _.get(data, 'value'); const changed = value !== this.selectedKeyId; @@ -191,22 +157,12 @@ class ScEnvironmentSshConnectionRow extends React.Component { networkInterfaces={networkInterfaces} keyName={selectedKeyName} connectionId={item.id} + scEnvironmentsStore={this.envsStore} + scEnvironment={this.environment} />, ); } - if (this.isAppStreamEnabled && !_.isUndefined(networkInterfaces)) { - rows.push( - - - - - , - ); - } - return rows; } } @@ -224,7 +180,6 @@ decorate(ScEnvironmentSshConnectionRow, { selectedKeyId: observable, networkInterfaces: observable, processingSendKey: observable, - appStreamUrl: observable, handleActivate: action, handleKeyChange: action, }); diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnections.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnections.js index 4b98408711..366627c139 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnections.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnections.js @@ -3,7 +3,7 @@ import React from 'react'; import { decorate, computed, action, runInAction, observable } from 'mobx'; import { observer, inject } from 'mobx-react'; import { withRouter } from 'react-router-dom'; -import { Segment, Icon, Button, Header, Table, Message } from 'semantic-ui-react'; +import { Segment, Icon, Button, Header, Table, Message, List } from 'semantic-ui-react'; import { swallowError } from '@aws-ee/base-ui/dist/helpers/utils'; import { isStoreLoading, isStoreError, isStoreReady } from '@aws-ee/base-ui/dist/models/BaseStore'; @@ -32,6 +32,10 @@ class ScEnvironmentSshConnections extends React.Component { } } + get isAppStreamEnabled() { + return process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true'; + } + get environment() { return this.props.scEnvironment; } @@ -75,6 +79,32 @@ class ScEnvironmentSshConnections extends React.Component { return
{content}
; } + renderAppStreamInfo() { + return ( + + Connection instructions for your AppStream workspace: + + Select your SSH key below. You must have downloaded this already. + Paste the key's contents into AppStream Notepad in .PEM format + Save the file in the Downloads folder in .PEM format named like 'KeyName.pem' + Open PuttyGen in AppStream and convert your private PEM key to PPK format + Enter the PPK file and private IP address in Putty to SSH into EC2 + Delete this file once EC2 connection is established + +
More information on connecting to your Linux instance from Windows OS:
+ + + Connecting from Windows via Putty + + +
+ ); + } + renderConnections() { const env = this.environment; const showCreateKey = this.showCreateKey; @@ -86,6 +116,7 @@ class ScEnvironmentSshConnections extends React.Component { return (
+ {this.isAppStreamEnabled && this.renderAppStreamInfo()} {empty && ( Attention! diff --git a/scripts/app-stream/SETUP.md b/scripts/app-stream/SETUP.md index 4a3fb4eedd..e61132198b 100644 --- a/scripts/app-stream/SETUP.md +++ b/scripts/app-stream/SETUP.md @@ -29,7 +29,7 @@ Note: Please set up your [AWS Profile](https://docs.aws.amazon.com/cli/latest/us cd ~\Documents # Pull the Image Builder script from Github -Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/feat-appstream-ui/scripts/app-stream/buildImage.ps1 -OutFile buildImage.ps1 +Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/mainline/scripts/app-stream/buildImage.ps1 -OutFile buildImage.ps1 # Execute Image builder script .\buildImage.ps1 diff --git a/scripts/app-stream/buildImage.ps1 b/scripts/app-stream/buildImage.ps1 index 5ef54454d7..2013568866 100644 --- a/scripts/app-stream/buildImage.ps1 +++ b/scripts/app-stream/buildImage.ps1 @@ -3,7 +3,7 @@ Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManage choco install -y putty.install -Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/feat-appstream-ui/scripts/app-stream/ec2linux.ps1 -OutFile 'C:\Users\Public\Documents\EC2Linux.ps1' +Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/mainline/scripts/app-stream/ec2linux.ps1 -OutFile 'C:\Users\Public\Documents\EC2Linux.ps1' # Add Applications From ed17bf01426a71d6661d289ce1703f13c733d60e Mon Sep 17 00:00:00 2001 From: SanketD92 Date: Thu, 15 Jul 2021 12:49:06 -0400 Subject: [PATCH 09/12] fix: single quotes to double --- .../environments-sc/parts/ScEnvironmentSshConnections.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnections.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnections.js index 366627c139..1bbbdf828c 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnections.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnections.js @@ -86,7 +86,9 @@ class ScEnvironmentSshConnections extends React.Component { Select your SSH key below. You must have downloaded this already. Paste the key's contents into AppStream Notepad in .PEM format - Save the file in the Downloads folder in .PEM format named like 'KeyName.pem' + + Save the file in the Downloads folder in .PEM format named like "KeyName.pem" (with quotes) + Open PuttyGen in AppStream and convert your private PEM key to PPK format Enter the PPK file and private IP address in Putty to SSH into EC2 Delete this file once EC2 connection is established From c81e46b3d7cb635ddc2617c4d4d4e7c1ff2e0176 Mon Sep 17 00:00:00 2001 From: SanketD92 Date: Thu, 15 Jul 2021 13:19:17 -0400 Subject: [PATCH 10/12] fix: update MobX observable in action --- .../parts/environments-sc/parts/ScEnvSshConnRowExpanded.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js index f96c8f477c..62602caaf3 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js @@ -101,7 +101,9 @@ class ScEnvSshConnRowExpanded extends React.Component { } else { throw Error('AppStream URL was not returned by the API'); } - this.processingId = id; + runInAction(() => { + this.processingId = id; + }); } catch (error) { displayError(error); } finally { From 6e2a5217713a165476e9fd4a0a168cabc6cda6d1 Mon Sep 17 00:00:00 2001 From: SanketD92 Date: Thu, 15 Jul 2021 13:20:11 -0400 Subject: [PATCH 11/12] fix: mobx observable fix --- .../environments-sc/parts/ScEnvironmentRdpConnectionRow.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js index ddfc3198cf..4ca184c8cd 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js @@ -91,7 +91,9 @@ class ScEnvironmentRdpConnectionRow extends React.Component { } else { throw Error('AppStream URL was not returned by the API'); } - this.processingId = id; + runInAction(() => { + this.processingId = id; + }); } catch (error) { displayError(error); } finally { From 42e29ca59c5048a1641c40c51adfc68bb87e872a Mon Sep 17 00:00:00 2001 From: SanketD92 Date: Thu, 15 Jul 2021 13:23:19 -0400 Subject: [PATCH 12/12] update observables --- .../parts/environments-sc/parts/ScEnvironmentHttpConnections.js | 2 ++ .../environments-sc/parts/ScEnvironmentRdpConnectionRow.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js index 1798cd3667..b6af9f8d57 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js @@ -23,6 +23,7 @@ class ScEnvironmentHttpConnections extends React.Component { // The id of the connection that is being processed this.processingId = ''; this.appStreamGeneratingId = ''; + this.appStreamConnectingId = ''; this.destinationUrl = undefined; this.streamingUrl = undefined; this.timeout = 10; @@ -285,6 +286,7 @@ decorate(ScEnvironmentHttpConnections, { connections: computed, processingId: observable, appStreamGeneratingId: observable, + appStreamConnectingId: observable, destinationUrl: observable, streamingUrl: observable, }); diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js index 4ca184c8cd..a07c416d0b 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js @@ -28,6 +28,7 @@ class ScEnvironmentRdpConnectionRow extends React.Component { this.processingGetInfo = false; // Should the password be shown this.showPassword = false; + this.processingId = undefined; }); } @@ -275,6 +276,7 @@ decorate(ScEnvironmentRdpConnectionRow, { connection: computed, connectionId: computed, networkInterfaces: computed, + processingId: observable, windowsRdpInfo: observable, processingGetInfo: observable, showPassword: observable,