When a container is opened from a stack, the detail tab kept the stack trail (PR #7) but the attribute sub-tabs (Logs, Stats, Inspect, Console, Attach) dropped it: those tabs were registered only under the global docker.containers.container.* tree, so navigating to one left the stack state (and its inherited params) behind, and each sub-view set a hardcoded "Containers > ..." breadcrumb. - Register stack-scoped child states docker.stacks.stack.container.{attach, exec,inspect,logs,stats} mirroring the global ones, so the inherited stack params survive and the trail can be kept. - Centralize the breadcrumb logic in containerBreadcrumbs.ts (moved out of ItemView, which re-exports it) and add isStackContainerState + getContainerSubTabBreadcrumbs + buildStackContainerLinkParams. - ActionLinksRow links sub-tabs into the stack tree (with stack+container params) when opened from a stack, else the global states unchanged. - InspectView + the logs/stats/console controllers render the stack-aware trail; set up-front (no name) so it survives the load window and errors. Covers regular/external/orphaned stacks and the non-stack fallback, matching the existing ItemView breadcrumb behavior. New unit tests in containerBreadcrumbs.test.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
92 lines
4.6 KiB
HTML
92 lines
4.6 KiB
HTML
<page-header title="'Container console'" breadcrumbs="breadcrumbs"> </page-header>
|
|
|
|
<div class="row" ng-init="initView()" ng-show="loaded">
|
|
<div class="col-lg-12 col-md-12 col-xs-12">
|
|
<rd-widget>
|
|
<rd-widget-header icon="terminal" title-text="Execute"></rd-widget-header>
|
|
<rd-widget-body>
|
|
<form class="form-horizontal">
|
|
<div ng-if="state === states.disconnected">
|
|
<!-- command-list -->
|
|
<div class="form-group">
|
|
<label for="command" class="col-lg-1 col-sm-2 control-label text-left">Command</label>
|
|
<div class="col-lg-11 col-sm-10">
|
|
<div class="input-group" ng-if="!formValues.isCustomCommand">
|
|
<span class="input-group-addon">
|
|
<pr-icon ng-if="imageOS == 'linux'" icon="'svg-linux'"></pr-icon>
|
|
<pr-icon ng-if="imageOS == 'windows'" icon="'layout-grid'"></pr-icon>
|
|
</span>
|
|
<select class="form-control" ng-model="formValues.command" id="command" data-cy="command-select">
|
|
<option value="ash" ng-if="imageOS == 'linux'">/bin/ash</option>
|
|
<option value="bash" ng-if="imageOS == 'linux'">/bin/bash</option>
|
|
<option value="dash" ng-if="imageOS == 'linux'">/bin/dash</option>
|
|
<option value="sh" ng-if="imageOS == 'linux'">/bin/sh</option>
|
|
<option value="powershell" ng-if="imageOS == 'windows'">powershell</option>
|
|
<option value="cmd.exe" ng-if="imageOS == 'windows'">cmd.exe</option>
|
|
<option ng-repeat="command in containerCommands" value="{{ command.command }}">{{ command.title }}: {{ command.command }}</option>
|
|
</select>
|
|
</div>
|
|
<input
|
|
class="form-control"
|
|
ng-if="formValues.isCustomCommand"
|
|
type="text"
|
|
name="custom-command"
|
|
ng-model="formValues.customCommand"
|
|
placeholder="e.g. ps aux"
|
|
data-cy="custom-command"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<!-- !command-list -->
|
|
<div class="form-group col-lg-12">
|
|
<por-switch-field
|
|
label-class="'col-sm-1'"
|
|
checked="formValues.isCustomCommand"
|
|
label="'Use custom command'"
|
|
on-change="(handleIsCustomCommandChange)"
|
|
></por-switch-field>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="col-lg-1 col-sm-2 control-label text-left">
|
|
User
|
|
<portainer-tooltip message="'Format is one of: user, user:group, uid or uid:gid'"></portainer-tooltip>
|
|
</label>
|
|
<div class="col-lg-11 col-sm-10">
|
|
<input class="form-control" type="text" ng-model="formValues.user" placeholder="root" data-cy="container-exec-user" />
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<div class="col-sm-12">
|
|
<button type="button" class="btn btn-primary" ng-disabled="!container.State.Running" ng-click="connectExec()">
|
|
<span>Connect</span>
|
|
</button>
|
|
<span class="small text-danger vertical-center" ng-hide="container.State.Running">
|
|
<pr-icon icon="'alert-triangle'" mode="'danger'"></pr-icon>
|
|
The container is not running.
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div ng-if="state !== states.disconnected">
|
|
<label
|
|
>Exec into container as <code class="align-baseline !text-sm">{{ ::formValues.user || 'default user' }}</code> using command
|
|
<code class="align-baseline !text-sm">{{ formValues.isCustomCommand ? formValues.customCommand : formValues.command }}</code>
|
|
<terminal-tooltip class="align-sub"> </terminal-tooltip>
|
|
</label>
|
|
<button type="button" class="btn btn-primary" ng-click="disconnect()">
|
|
<span ng-show="state === states.connected">Disconnect</span>
|
|
<span ng-show="state === states.connecting">Connecting...</span>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</rd-widget-body>
|
|
</rd-widget>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-lg-12 col-md-12 col-xs-12">
|
|
<shell-terminal url="shellUrl" connect="shellConnect" on-state-change="(onShellStateChange)" on-resize="(onShellResize)" initial-commands="shellInitCommands"></shell-terminal>
|
|
</div>
|
|
</div>
|