feat: Connect to a host within target
This commit is contained in:
9 files changed
+101
-10
No files matched your search
@@ -99,6 +99,7 @@ host:
|
||||
add: Add Host
|
||||
create: New Host
|
||||
delete: Delete Host
|
||||
connect: Connect With Host
|
||||
types:
|
||||
static: Static
|
||||
session:
|
||||
|
||||
@@ -100,9 +100,11 @@ export default class ScopesScopeProjectsTargetsRoute extends Route {
|
||||
|
||||
/**
|
||||
* Establish a session to current target.
|
||||
* @param {TargetModel} model
|
||||
*/
|
||||
@action
|
||||
async connect(model) {
|
||||
// TODO: Connect: Refactor into an addon
|
||||
try {
|
||||
// Check for CLI
|
||||
const cliExists = await this.ipc.invoke('cliExists');
|
||||
|
||||
@@ -34,13 +34,18 @@ export default class ScopesScopeProjectsTargetsTargetRoute extends Route {
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the target-specific templates for header and navigation
|
||||
* Renders the target-specific templates for actions, header and navigation
|
||||
* page sections.
|
||||
* @override
|
||||
*/
|
||||
renderTemplate() {
|
||||
super.renderTemplate(...arguments);
|
||||
|
||||
this.render('scopes/scope/projects/targets/target/-actions', {
|
||||
into: 'scopes/scope/projects/targets/target',
|
||||
outlet: 'actions',
|
||||
});
|
||||
|
||||
this.render('scopes/scope/projects/targets/target/-header', {
|
||||
into: 'scopes/scope/projects/targets/target',
|
||||
outlet: 'header',
|
||||
@@ -50,6 +55,5 @@ export default class ScopesScopeProjectsTargetsTargetRoute extends Route {
|
||||
into: 'scopes/scope/projects/targets/target',
|
||||
outlet: 'navigation',
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,18 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { all } from 'rsvp';
|
||||
import { all, hash } from 'rsvp';
|
||||
import { later } from '@ember/runloop';
|
||||
import { action } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class ScopesScopeProjectsTargetsTargetHostsRoute extends Route {
|
||||
// =services
|
||||
|
||||
@service ipc;
|
||||
@service session;
|
||||
@service origin;
|
||||
@service notify;
|
||||
@service confirm;
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
@@ -26,10 +37,69 @@ export default class ScopesScopeProjectsTargetsTargetHostsRoute extends Route {
|
||||
|
||||
// Load unique hosts
|
||||
const uniqueHostIds = new Set(hostIds.flat());
|
||||
return all(
|
||||
[...uniqueHostIds].map((hostId) =>
|
||||
this.store.findRecord('host', hostId)
|
||||
|
||||
return hash({
|
||||
target,
|
||||
hosts: all(
|
||||
[...uniqueHostIds].map((hostId) =>
|
||||
this.store.findRecord('host', hostId)
|
||||
)
|
||||
)
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish a session to host within target.
|
||||
* @param {TargetModel} target
|
||||
* @param {HostModel} host
|
||||
*/
|
||||
@action
|
||||
async connect(target, host) {
|
||||
// TODO: Connect: Refactor into an addon
|
||||
try {
|
||||
// Check for CLI
|
||||
const cliExists = await this.ipc.invoke('cliExists');
|
||||
if (!cliExists) throw new Error('Cannot find Boundary CLI.');
|
||||
|
||||
// Create target session
|
||||
const connectionDetails = await this.ipc.invoke('connect', {
|
||||
host_id: host.id,
|
||||
target_id: target.id,
|
||||
token: this.session.data.authenticated.token,
|
||||
addr: this.origin.rendererOrigin
|
||||
});
|
||||
|
||||
// Associate the connection details with the session
|
||||
const { session_id, address, port } = connectionDetails;
|
||||
await this.store.findRecord('session', session_id);
|
||||
|
||||
// Update the session record with proxy information from the CLI
|
||||
// This is read-only information that shouldn't dirty the session,
|
||||
// so we use store.push here. In the future, it may make sense to push
|
||||
// this off to the API so that we don't have to manually persist the
|
||||
// proxy details.
|
||||
later(() => {
|
||||
this.store.push({
|
||||
data: {
|
||||
id: session_id,
|
||||
type: 'session',
|
||||
attributes: {
|
||||
proxy_address: address,
|
||||
proxy_port: port
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 150);
|
||||
|
||||
// Show the user a modal with basic connection info.
|
||||
// We don't await because this modal is purely informational.
|
||||
this.confirm.confirm(connectionDetails, { isConnectSuccess: true });
|
||||
|
||||
} catch(e) {
|
||||
this.confirm.confirm(e.message, { isConnectError: true })
|
||||
// Retry
|
||||
.then(() => this.connect(target, host))
|
||||
.catch(() => null /* no op */);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,10 @@
|
||||
{{outlet "header"}}
|
||||
</page.header>
|
||||
|
||||
<page.actions>
|
||||
{{outlet "actions"}}
|
||||
</page.actions>
|
||||
|
||||
<page.navigation>
|
||||
{{outlet "navigation"}}
|
||||
</page.navigation>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<Rose::Button @style="primary" {{on "click" (route-action "connect" @model)}}>
|
||||
{{t "resources.session.actions.connect"}}
|
||||
</Rose::Button>
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<Rose::Table as |table|>
|
||||
<table.body as |body|>
|
||||
{{#each @model as |host|}}
|
||||
{{#each @model.hosts as |host|}}
|
||||
<body.row as |row|>
|
||||
<row.cell>
|
||||
{{host.displayName}}
|
||||
@@ -21,6 +21,11 @@
|
||||
<row.cell>
|
||||
<Rose::Badge>{{t (concat 'resources.host.types.' host.type)}}</Rose::Badge>
|
||||
</row.cell>
|
||||
<row.cell>
|
||||
<Rose::Button @style="secondary" {{on "click" (route-action "connect" @model.target host)}}>
|
||||
{{t "resources.host.actions.connect"}}
|
||||
</Rose::Button>
|
||||
</row.cell>
|
||||
</body.row>
|
||||
{{/each}}
|
||||
</table.body>
|
||||
|
||||
@@ -7,7 +7,7 @@ module.exports = {
|
||||
// Check boundary cli existence
|
||||
exists: () => Boolean(cliPath()),
|
||||
// Initiate connection and return output
|
||||
connect: (target_id, token, addr) => {
|
||||
connect: (target_id, token, addr, host_id) => {
|
||||
const command = [
|
||||
'connect',
|
||||
`-target-id=${target_id}`,
|
||||
@@ -16,6 +16,8 @@ module.exports = {
|
||||
'-format=json',
|
||||
'--output-json-errors'
|
||||
];
|
||||
|
||||
if(host_id) command.push(`-host-id=${host_id}`)
|
||||
return spawnAsyncJSONPromise(command);
|
||||
},
|
||||
// Returns JSON-formatted version information from the CLI
|
||||
|
||||
@@ -25,4 +25,4 @@ handle('cliExists', () => boundaryCli.exists());
|
||||
/**
|
||||
* Establishes a boundary session and returns session details.
|
||||
*/
|
||||
handle('connect', ({ target_id, token, addr }) => boundaryCli.connect(target_id, token, addr));
|
||||
handle('connect', ({ target_id, token, addr, host_id }) => boundaryCli.connect(target_id, token, addr, host_id));
|
||||
Reference in new issue
Block a user