Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Account ids #852

Merged
merged 9 commits into from
Mar 25, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions src/server_manager/model/digitalocean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export enum Status {
}

export interface Account {
// Gets a globally unique identifier for this Account.
getId(): string;
// Returns a user-friendly name (email address) associated with the account.
getName(): Promise<string>;
// Returns the status of the account.
Expand Down
4 changes: 1 addition & 3 deletions src/server_manager/model/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// limitations under the License.

export interface Server {
// Gets the server ID.
// Gets a globally unique identifier for this Server.
getId(): string;

// Gets the server's name for display.
Expand Down Expand Up @@ -122,8 +122,6 @@ export interface ManagedServerHost {
getRegionId(): RegionId;
// Deletes the server - cannot be undone.
delete(): Promise<void>;
// Returns the virtual host ID.
getHostId(): string;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we update the Server#getId method to mention that this ID is unique across clouds?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


export class DataAmount {
Expand Down
32 changes: 20 additions & 12 deletions src/server_manager/web_app/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,14 +340,17 @@ export class App {
}
try {
this.digitalOceanAccount = digitalOceanAccount;
this.appRoot.digitalOceanAccountName = await this.digitalOceanAccount.getName();
this.appRoot.digitalOceanAccount = {
id: this.digitalOceanAccount.getId(),
name: await this.digitalOceanAccount.getName()
};
const status = await this.digitalOceanAccount.getStatus();
if (status !== digitalocean.Status.ACTIVE) {
return [];
}
const servers = await this.digitalOceanAccount.listServers();
for (const server of servers) {
this.addServer(server);
this.addServer(this.digitalOceanAccount.getId(), server);
}
return servers;
} catch (error) {
Expand All @@ -360,15 +363,15 @@ export class App {

private async loadManualServers() {
for (const server of await this.manualServerRepository.listServers()) {
this.addServer(server);
this.addServer(null, server);
}
}

private makeServerListEntry(server: server.Server): ServerListEntry {
private makeServerListEntry(accountId: string, server: server.Server): ServerListEntry {
return {
id: server.getId(),
accountId,
name: this.makeDisplayName(server),
isManaged: isManagedServer(server),
isSynced: !!server.getName(),
};
}
Expand All @@ -384,10 +387,10 @@ export class App {
return name;
}

private addServer(server: server.Server): void {
private addServer(accountId: string, server: server.Server): void {
console.log('Loading server', server);
this.idServerMap.set(server.getId(), server);
const serverEntry = this.makeServerListEntry(server);
const serverEntry = this.makeServerListEntry(accountId, server);
this.appRoot.serverList = this.appRoot.serverList.concat([serverEntry]);

if (isManagedServer(server) && !server.isInstallCompleted()) {
Expand Down Expand Up @@ -428,7 +431,7 @@ export class App {

private updateServerEntry(server: server.Server): void {
this.appRoot.serverList = this.appRoot.serverList.map(
(ds) => ds.id === server.getId() ? this.makeServerListEntry(server) : ds);
(ds) => ds.id === server.getId() ? this.makeServerListEntry(ds.accountId, server) : ds);
}

private getServerById(serverId: string): server.Server {
Expand Down Expand Up @@ -607,14 +610,19 @@ export class App {

// Clears the DigitalOcean credentials and returns to the intro screen.
private disconnectDigitalOceanAccount(): void {
if (!this.digitalOceanAccount) {
// Not connected.
return;
}
const accountId = this.digitalOceanAccount.getId();
this.cloudAccounts.disconnectDigitalOceanAccount();
this.digitalOceanAccount = null;
for (const serverEntry of this.appRoot.serverList) {
if (serverEntry.isManaged) {
if (serverEntry.accountId === accountId) {
this.removeServer(serverEntry.id);
}
}
this.appRoot.digitalOceanAccountName = '';
this.appRoot.digitalOceanAccount = null;
}

// Clears the GCP credentials and returns to the intro screen.
Expand Down Expand Up @@ -665,7 +673,7 @@ export class App {
const server = await this.digitalOceanRetry(() => {
return this.digitalOceanAccount.createServer(regionId, serverName);
});
this.addServer(server);
this.addServer(this.digitalOceanAccount.getId(), server);
this.showServer(server);
} catch (error) {
console.error('Error from createDigitalOceanServer', error);
Expand Down Expand Up @@ -1051,7 +1059,7 @@ export class App {
}
const manualServer = await this.manualServerRepository.addServer(serverConfig);
if (await manualServer.isHealthy()) {
this.addServer(manualServer);
this.addServer(null, manualServer);
this.showServer(manualServer);
} else {
// Remove inaccessible manual server from local storage if it was just created.
Expand Down
2 changes: 1 addition & 1 deletion src/server_manager/web_app/cloud_accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export class CloudAccounts implements accounts.CloudAccounts {
}

private createDigitalOceanAccount(accessToken: string): DigitalOceanAccount {
return new DigitalOceanAccount(accessToken, this.shadowboxSettings, this.isDebugMode);
return new DigitalOceanAccount('do', accessToken, this.shadowboxSettings, this.isDebugMode);
}

private createGcpAccount(refreshToken: string): GcpAccount {
Expand Down
9 changes: 7 additions & 2 deletions src/server_manager/web_app/digitalocean_account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@ export class DigitalOceanAccount implements digitalocean.Account {
private servers: DigitalOceanServer[] = [];

constructor(
private accessToken: string, private shadowboxSettings: ShadowboxSettings,
private id: string, private accessToken: string, private shadowboxSettings: ShadowboxSettings,
private debugMode: boolean) {
this.digitalOcean = new RestApiSession(accessToken);
}

getId(): string {
return this.id;
}

async getName(): Promise<string> {
return (await this.digitalOcean.getAccount())?.email;
}
Expand Down Expand Up @@ -121,7 +125,8 @@ export class DigitalOceanAccount implements digitalocean.Account {

// Creates a DigitalOceanServer object and adds it to the in-memory server list.
private createDigitalOceanServer(digitalOcean: DigitalOceanSession, dropletInfo: DropletInfo) {
const server = new DigitalOceanServer(digitalOcean, dropletInfo);
const server =
new DigitalOceanServer(`${this.id}:${dropletInfo.id}`, digitalOcean, dropletInfo);
fortuna marked this conversation as resolved.
Show resolved Hide resolved
this.servers.push(server);
return server;
}
Expand Down
9 changes: 3 additions & 6 deletions src/server_manager/web_app/digitalocean_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,11 @@ export class DigitalOceanServer extends ShadowboxServer implements server.Manage
private eventQueue = new EventEmitter();
private installState: InstallState = InstallState.UNKNOWN;

constructor(private digitalOcean: DigitalOceanSession, private dropletInfo: DropletInfo) {
constructor(
id: string, private digitalOcean: DigitalOceanSession, private dropletInfo: DropletInfo) {
// Consider passing a RestEndpoint object to the parent constructor,
// to better encapsulate the management api address logic.
super(String(dropletInfo.id));
super(id);
console.info('DigitalOceanServer created');
this.eventQueue.once('server-active', () => console.timeEnd('activeServer'));
this.pollInstallState();
Expand Down Expand Up @@ -306,10 +307,6 @@ class DigitalOceanHost implements server.ManagedServerHost {
this.deleteCallback();
});
}

getHostId(): string {
return `${this.dropletInfo.id}`;
}
}

function startsWithCaseInsensitive(text: string, prefix: string) {
Expand Down
7 changes: 4 additions & 3 deletions src/server_manager/web_app/manual_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ import {ShadowboxServer} from './shadowbox_server';

class ManualServer extends ShadowboxServer implements server.ManualServer {
constructor(
private manualServerConfig: server.ManualServerConfig, private forgetCallback: Function) {
super(manualServerConfig.apiUrl);
id: string, private manualServerConfig: server.ManualServerConfig,
private forgetCallback: Function) {
super(id);
this.setManagementApiUrl(manualServerConfig.apiUrl);
// manualServerConfig.certSha256 is expected to be in hex format (install script).
// Electron requires that this be decoded from hex (to unprintable binary),
Expand Down Expand Up @@ -92,7 +93,7 @@ export class ManualServerRepository implements server.ManualServerRepository {
}

private createServer(config: server.ManualServerConfig) {
const server = new ManualServer(config, () => {
const server = new ManualServer(`manual:${config.apiUrl}`, config, () => {
this.forgetServer(server);
});
return server;
Expand Down
4 changes: 4 additions & 0 deletions src/server_manager/web_app/testing/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export class FakeDigitalOceanAccount implements digitalocean.Account {

constructor(private accessToken = 'fake-access-token') {}

getId(): string {
return 'account-id';
}

async getName(): Promise<string> {
return 'fake-digitalocean-account-name';
}
Expand Down
44 changes: 29 additions & 15 deletions src/server_manager/web_app/ui_components/app-root.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,19 @@ import {ServerView} from './outline-server-view.js';

const TOS_ACK_LOCAL_STORAGE_KEY = 'tos-ack';

/**
* A cloud account to be displayed
* @typedef {Object} AccountListEntry
* @prop {string} id
* @prop {string} name
*/

/**
* An access key to be displayed
* @typedef {Object} ServerListEntry
* @prop {string} id
* @prop {string|null} accountId
fortuna marked this conversation as resolved.
Show resolved Hide resolved
* @prop {string} name
* @prop {boolean} isManaged
* @prop {boolean} isSynced
*/

Expand Down Expand Up @@ -382,7 +389,7 @@ export class AppRoot extends mixinBehaviors
<app-header-layout>
<div class="app-container">
<iron-pages attr-for-selected="id" selected="{{ currentPage }}">
<outline-intro-step id="intro" digital-ocean-account-name="{{digitalOceanAccountName}}" gcp-account-name="{{gcpAccountName}}" localize="[[localize]]"></outline-intro-step>
<outline-intro-step id="intro" digital-ocean-account-name="{{digitalOceanAccount.name}}" gcp-account-name="{{gcpAccountName}}" localize="[[localize]]"></outline-intro-step>
<outline-do-oauth-step id="digitalOceanOauth" localize="[[localize]]"></outline-do-oauth-step>
<outline-gcp-oauth-step id="gcpOauth" localize="[[localize]]"></outline-gcp-oauth-step>
<outline-manual-server-entry id="manualEntry" localize="[[localize]]"></outline-manual-server-entry>
Expand Down Expand Up @@ -446,27 +453,28 @@ export class AppRoot extends mixinBehaviors
static expandedServersTemplate() {
return html`
<!-- DigitalOcean servers -->
<div class="servers-section" hidden\$="[[!digitalOceanAccountName]]">
<div class="servers-section" hidden\$="[[!digitalOceanAccount]]">
<div class="servers-header">
<span>[[localize('servers-digitalocean')]]</span>
<paper-menu-button horizontal-align="left" class="" close-on-activate="" no-animations="" dynamic-align="" no-overlap="">
<paper-icon-button icon="more-vert" slot="dropdown-trigger"></paper-icon-button>
<div class="do-overflow-menu" slot="dropdown-content">
<h4>[[localize('digitalocean-disconnect-account')]]</h4>
<div class="account-info"><img src="images/digital_ocean_logo.svg">[[digitalOceanAccountName]]</div>
<div class="account-info"><img src="images/digital_ocean_logo.svg">[[digitalOceanAccount.name]]</div>
<div class="sign-out-button" on-tap="signOutTapped">[[localize('digitalocean-disconnect')]]</div>
</div>
</paper-menu-button>
</div>
<div class="servers-container">
<template is="dom-repeat" items="[[serverList]]" as="server" filter="_isServerManaged" sort="_sortServersByName">
<template is="dom-repeat" items="[[serverList]]" as="server" filter="[[_accountServerFilter(digitalOceanAccount)]]" sort="_sortServersByName">
<div class\$="server [[_computeServerClasses(selectedServerId, server)]]" data-server\$="[[server]]" on-tap="_showServer">
<img class="server-icon" src\$="images/[[_computeServerImage(selectedServerId, server)]]">
<span>[[server.name]]</span>
</div>
</template>
</div>
</div>
<!-- TODO(fortuna): Insert GCP servers here -->
<!-- Manual servers -->
<div class="servers-section" hidden\$="[[!_hasManualServers(serverList)]]">
<div class="servers-header">
Expand All @@ -487,14 +495,15 @@ export class AppRoot extends mixinBehaviors
static minimizedServersTemplate() {
return html`
<!-- DigitalOcean servers -->
<div class="side-bar-section servers-section" hidden\$="[[!digitalOceanAccountName]]">
<div class="side-bar-section servers-section" hidden\$="[[!digitalOceanAccount]]">
<img class="provider-icon" src="images/do_white_logo.svg">
<template is="dom-repeat" items="[[serverList]]" as="server" filter="_isServerManaged" sort="_sortServersByName">
<template is="dom-repeat" items="[[serverList]]" as="server" filter="[[_accountServerFilter(digitalOceanAccount)]]" sort="_sortServersByName">
<div class\$="server [[_computeServerClasses(selectedServerId, server)]]" data-server\$="[[server]]" on-tap="_showServer">
<img class="server-icon" src\$="images/[[_computeServerImage(selectedServerId, server)]]">
</div>
</template>
</div>
<!-- TODO(fortuna): Insert GCP servers here -->
<!-- Manual servers -->
<div class="side-bar-section servers-section" hidden\$="[[!_hasManualServers(serverList)]]">
<img class="provider-icon" src="images/cloud.svg">
Expand All @@ -520,8 +529,8 @@ export class AppRoot extends mixinBehaviors
useKeyIfMissing: {type: Boolean},
serverList: {type: Array},
selectedServerId: {type: String},
digitalOceanAccountName: String,
gcpAccountName: String,
digitalOceanAccount: Object,
gcpAccount: Object,
outlineVersion: String,
userAcceptedTos: {
type: Boolean,
Expand Down Expand Up @@ -550,8 +559,10 @@ export class AppRoot extends mixinBehaviors
this.useKeyIfMissing = true;
/** @type {ServerListEntry[]} */
this.serverList = [];
this.digitalOceanAccountName = '';
this.gcpAccountName = '';
/** @type {AccountListEntry} */
this.digitalOceanAccount = null;
/** @type {AccountListEntry} */
this.gcpAccount = null;
this.outlineVersion = '';
this.currentPage = 'intro';
this.shouldShowSideBar = false;
Expand Down Expand Up @@ -761,7 +772,7 @@ export class AppRoot extends mixinBehaviors
}

_hasManualServers(serverList) {
return serverList.filter(server => !server.isManaged).length > 0;
return serverList.filter(server => !server.accountId).length > 0;
}

_userAcceptedTosChanged(userAcceptedTos) {
Expand Down Expand Up @@ -896,12 +907,15 @@ export class AppRoot extends mixinBehaviors
return shouldShowSideBar ? 'side-bar-margin' : '';
}

_isServerManaged(server) {
return server.isManaged;
/**
* @param {AccountListEntry} account
*/
_accountServerFilter(account) {
return (server) => account && server.accountId === account.id;
}

_isServerManual(server) {
return !server.isManaged;
return !server.accountId;
}

_sortServersByName(a, b) {
Expand Down