Skip to content
This repository has been archived by the owner on Dec 6, 2024. It is now read-only.

feat: appstream connect UI changes #580

Merged
merged 4 commits into from
Jul 15, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const Service = require('@aws-ee/base-services-container/lib/service');

const settingKeys = {
appStreamImageName: 'appStreamImageName',
isAppStreamEnabled: 'isAppStreamEnabled',
};

class AppStreamScService extends Service {
Expand Down Expand Up @@ -116,9 +115,6 @@ class AppStreamScService extends Service {
}

async getStreamingUrl(requestContext, { environmentId, applicationId }) {
const isAppStreamEnabled = this.settings.get(settingKeys.isAppStreamEnabled);
if (isAppStreamEnabled !== 'true') return undefined;

const environmentScService = await this.service('environmentScService');

const appStream = await environmentScService.getClientSdkWithEnvMgmtRole(
Expand Down Expand Up @@ -150,9 +146,6 @@ class AppStreamScService extends Service {
}

async urlForRemoteDesktop(requestContext, { environmentId, instanceId }) {
const isAppStreamEnabled = this.settings.get(settingKeys.isAppStreamEnabled);
if (isAppStreamEnabled !== 'true') return undefined;

const environmentScService = await this.service('environmentScService');
const environment = await environmentScService.mustFind(requestContext, { id: environmentId });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,24 @@

const _ = require('lodash');

const settingKeys = {
isAppStreamEnabled: 'isAppStreamEnabled',
};

async function createConnectionUrl({ envId, connection }, { requestContext, container }) {
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');
const settings = await container.find('settings');
const isAppStreamEnabled = settings.optionalBoolean(settingKeys.isAppStreamEnabled, false);

// This plugin will only contribute to URL creation when AppStream is enabled
// Since this plugin is also called upon during listConnections cycle
// it will only be triggered during the URL creation API call
if (!isAppStreamEnabled || connection.operation === 'list') {
return { envId, connection };
}

// Only wrap via AppStream if the connection.url exists
let appStreamUrl;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"source-map-support": "^0.5.16"
},
"scripts": {
"coverage": "NODE_ENV=test jest --config jest.config.js --passWithNoTests --verbose --collectCoverage --watchAll=false --coverage && codecov --disable=gcov",
"test": "NODE_ENV=test jest --config jest.config.js --passWithNoTests",
"test:watch": "NODE_ENV=test jest --config jest.config.js --passWithNoTests --watchAll",
"lint": "pnpm run lint:eslint && pnpm run lint:prettier",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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, Grid, Table, List } from 'semantic-ui-react';
import { Segment, Icon, Button, Header, Table, List } from 'semantic-ui-react';

import { displayError } from '@aws-ee/base-ui/dist/helpers/notification';

Expand Down Expand Up @@ -137,6 +137,7 @@ class ScEnvironmentHttpConnections extends React.Component {

return (
<div className="mt2 mb2">
{this.renderAppstreamInstructions(_.first(connections))}
<Table celled>
<Table.Header>
<Table.Row key={env.id}>
Expand All @@ -153,22 +154,22 @@ class ScEnvironmentHttpConnections extends React.Component {
const appStreamGeneratingId = this.appStreamGeneratingId;
const streamingUrl = this.streamingUrl;
const destinationUrl = this.destinationUrl;
const isDisabled = id => appStreamGeneratingId !== id && !_.isEmpty(appStreamGeneratingId);
const isLoading = id => appStreamGeneratingId === id;
const appStreamConnectingId = this.appStreamConnectingId;
const isDisabled = (id1, id2) => id2 !== id1 && !_.isEmpty(id2);
const isLoading = (id1, id2) => id2 === id1;

return (
<>
{_.map(connections, item => (
<>
{this.renderAppstreamInstructions(item)}
<Table.Row key={item.id}>
<Table.Cell className="clearfix">
<Button
floated="right"
size="mini"
primary
disabled={isDisabled(item.id)}
loading={isLoading(item.id)}
disabled={isDisabled(item.id, appStreamGeneratingId)}
loading={isLoading(item.id, appStreamGeneratingId)}
onClick={this.handleGenerateAppStreamUrl(item.id)}
>
Generate URL
Expand All @@ -179,35 +180,24 @@ class ScEnvironmentHttpConnections extends React.Component {
</Table.Row>

{destinationUrl && (
<>
<Table.Row key={`${item.id}_destination`} className="fadeIn animated">
<Table.Cell colSpan="3" className="p3">
<Grid columns={2} stackable key={`${item.id}__2`}>
<Grid.Row stretched>
<Grid.Column width={12}>
<div>
Click on this icon to copy the workspace destination URL:
<CopyToClipboard text={destinationUrl} />
</div>
</Grid.Column>
</Grid.Row>
</Grid>
</Table.Cell>
</Table.Row>

<Table.Row key={`${item.id}__3`}>
<Table.Cell>
<Button
floated="right"
size="mini"
primary
onClick={this.handleAppStreamConnect(streamingUrl, item.id)}
>
Connect
</Button>
</Table.Cell>
</Table.Row>
</>
<Table.Row key={`${item.id}_destination`} className="fadeIn animated">
<Table.Cell colSpan="3" className="p3">
<div>
Click on this icon to copy the workspace destination URL:
<CopyToClipboard text={destinationUrl} />
</div>
<Button
floated="right"
size="mini"
primary
disabled={isDisabled(item.id, appStreamConnectingId)}
loading={isLoading(item.id, appStreamConnectingId)}
onClick={this.handleAppStreamConnect(streamingUrl, item.id)}
>
Connect
</Button>
</Table.Cell>
</Table.Row>
)}
</>
))}
Expand Down Expand Up @@ -251,18 +241,16 @@ class ScEnvironmentHttpConnections extends React.Component {

renderAppstreamInstructions(item) {
return (
<>
<Table.Row key={`${item.id}__4`}>
<Table.Cell className="clearfix">
<b>Connection instructions for your AppStream workspace:</b>
<List bulleted>
<List.Item>Click the &apos;Generate URL&apos; button to start an AppStream Firefox session</List.Item>
<List.Item>Copy the destination URL that becomes available below</List.Item>
<List.Item>Paste (Ctrl + V) this destination URL in the new AppStream FireFox tab</List.Item>
</List>
</Table.Cell>
</Table.Row>
</>
this.isAppStreamEnabled && (
<Segment key={`${item.id}__4`} className="clearfix">
<b>Connection instructions for your AppStream workspace:</b>
<List bulleted>
<List.Item>Click the &quot;Generate URL&quot; button to create the designation instance URL</List.Item>
<List.Item>Copy the destination URL that becomes available below</List.Item>
<List.Item>Hit &quot;Connect&quot;. Paste the URL in the new AppStream FireFox tab</List.Item>
</List>
</Segment>
)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,11 @@ class ScEnvironmentRdpConnectionRow extends React.Component {
const store = this.getConnectionStore();
const connectionId = this.connectionId;

this.windowsRdpInfo = undefined;
this.showPassword = false;
this.processingGetInfo = true;
runInAction(() => {
this.windowsRdpInfo = undefined;
this.showPassword = false;
this.processingGetInfo = true;
});

try {
const result = await store.getWindowsRdpInfo(connectionId);
Expand Down Expand Up @@ -173,11 +175,7 @@ class ScEnvironmentRdpConnectionRow extends React.Component {
defined below.
</b>
<List bulleted>
{this.isAppStreamEnabled ? (
<>
<List.Item>Click the &apos;Connect&apos; button to navigate to the AppStream instance</List.Item>
</>
) : (
{!this.isAppStreamEnabled && (
<List.Item>
The IP Address or DNS of the instance.{' '}
{moreThanOne ? 'Ask your administrator if you are not sure which one to use:' : ''}
Expand Down Expand Up @@ -227,7 +225,14 @@ class ScEnvironmentRdpConnectionRow extends React.Component {
{this.isAppStreamEnabled && windowsRdpInfo && (
<Table.Row>
<Table.Cell>
<Button primary size="mini" onClick={this.handleConnect(connectionId)} floated="right">
<Button
primary
size="mini"
onClick={this.handleConnect(connectionId)}
floated="right"
disabled={this.processingId}
loading={this.processingId}
>
Connect
</Button>
</Table.Cell>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from 'react';
import { decorate, computed } from 'mobx';
import { observer, inject } from 'mobx-react';
import { withRouter } from 'react-router-dom';
import { Segment, Icon, Header, Table } from 'semantic-ui-react';
import { Segment, Icon, Header, Table, List } from 'semantic-ui-react';

import ScEnvironmentRdpConnectionRow from './ScEnvironmentRdpConnectionRow';

Expand All @@ -22,6 +22,10 @@ class ScEnvironmentRdpConnections extends React.Component {
return connections;
}

get isAppStreamEnabled() {
return process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true';
}

render() {
const env = this.environment;
const state = env.state;
Expand All @@ -40,12 +44,30 @@ class ScEnvironmentRdpConnections extends React.Component {
return <div className="fadeIn animated">{content}</div>;
}

renderAppstreamInstructions(item) {
return (
this.isAppStreamEnabled && (
<Segment key={`${item.id}__4`} className="clearfix">
<b>Connection instructions for your AppStream workspace:</b>
<List bulleted>
<List.Item>Click the &quot;Get Password&quot; button to retrieve user credentials for RDP</List.Item>
<List.Item>Copy credential information and Hit &quot;Connect&quot;</List.Item>
<List.Item>
A new AppStream session window will open with RDP login page. Paste the copied credentials
</List.Item>
</List>
</Segment>
)
);
}

renderConnections() {
const env = this.environment;
const connections = this.connections;

return (
<div className="mt2 mb2 fadeIn animated">
{this.renderAppstreamInstructions(_.first(connections))}
<Table celled>
<Table.Header>
<Table.Row>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,39 @@ class ScEnvironmentSshConnections extends React.Component {
<Segment>
<b>Connection instructions for your AppStream workspace:</b>
<List bulleted>
<List.Item>Select your SSH key below. You must have downloaded this already.</List.Item>
<List.Item>Paste the key&apos;s contents into AppStream Notepad in .PEM format</List.Item>
<List.Item>Select your SSH key below. Click &quot;Use this SSH key&quot; below</List.Item>
<List.Item>A private IP will be displayed. This will be used for Putty connection</List.Item>
<List.Item>Click &quot;Connect&quot; to open an AppStream session window</List.Item>
<List.Item>
Save the file in the Downloads folder in .PEM format named like &quot;KeyName.pem&quot; (with quotes)
Copy your private SSH key to AppStream
<List bulleted>
<List.Item>You must have downloaded the selected SSH key during creating it</List.Item>
<List.Item>Paste your SSH key&apos;s contents into Notepad in AppStream</List.Item>
<List.Item>
Save the file in the Downloads folder named like &quot;<i>KeyName</i>.pem&quot; (with quotes)
</List.Item>
</List>
</List.Item>
<List.Item>Open PuttyGen in AppStream and convert your private PEM key to PPK format</List.Item>
<List.Item>Enter the PPK file and private IP address in Putty to SSH into EC2</List.Item>
<List.Item>Delete this file once EC2 connection is established</List.Item>
<List.Item>
Convert your private key to PPK format.
<List bulleted>
<List.Item>
PuttyGen will already be open in AppStream window. Click &quot;Load&quot; and select your PEM file
</List.Item>
<List.Item>
Click on &quot;Save private key&quot;. Click &quot;Yes&quot; to save without passphrase{' '}
</List.Item>
</List>
</List.Item>
<List.Item>
Use PPK file in Putty
<List bulleted>
<List.Item>Enter the private IP address in Putty and select SSH connection type</List.Item>
<List.Item>In the Category pane, expand Connection, expand SSH, and then choose Auth</List.Item>
<List.Item>Browse and select your PPK file for authentication. Click Open</List.Item>
</List>
</List.Item>
<List.Item>Delete your PEM and PPK files once EC2 connection is established</List.Item>
</List>
<div className="mt3">More information on connecting to your Linux instance from Windows OS:</div>
<List bulleted>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ class EnvironmentScConnectionService extends Service {
// Give plugins chance to adjust the connection (such as connection url etc)
const adjustedConnections = await Promise.all(
_.map(result, async connection => {
// This is done so that plugins know it was called during list connections cycle
connection.operation = 'list';

const pluginsResult = await pluginRegistryService.visitPlugins(
'env-sc-connection-url',
'createConnectionUrl',
Expand Down Expand Up @@ -161,6 +164,8 @@ class EnvironmentScConnectionService extends Service {
connection.url = await this.getRStudioUrl(requestContext, envId, connection);
}

// This is done so that plugins know it was called during create URL cycle
connection.operation = 'create';
// Give plugins chance to adjust the connection (such as connection url etc)
const result = await pluginRegistryService.visitPlugins(
'env-sc-connection-url',
Expand Down