Skip to content

Commit

Permalink
NAS-132750 / 25.04 / Minor improvements for containers (#11102)
Browse files Browse the repository at this point in the history
  • Loading branch information
undsoft authored Nov 27, 2024
1 parent 485593d commit b70831e
Show file tree
Hide file tree
Showing 105 changed files with 1,310 additions and 828 deletions.
2 changes: 2 additions & 0 deletions src/app/enums/virtualization.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ export const virtualizationTypeLabels = new Map<VirtualizationType, string>([
export enum VirtualizationStatus {
Running = 'RUNNING',
Stopped = 'STOPPED',
Unknown = 'UNKNOWN',
}

export const virtualizationStatusLabels = new Map<VirtualizationStatus, string>([
[VirtualizationStatus.Running, T('Running')],
[VirtualizationStatus.Stopped, T('Stopped')],
[VirtualizationStatus.Unknown, T('Unknown')],
]);

export enum VirtualizationRemote {
Expand Down
6 changes: 6 additions & 0 deletions src/app/helptext/virtualization/containers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { marker as T } from '@biesbjerg/ngx-translate-extract-marker';

export const containersHelptext = {
cpuHint: T('Leave empty to allow all host CPUs to be used.'),
memoryHint: T('Leave empty to not limit instance memory.'),
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@
<ix-icon [name]="tile.failedSend ? 'mdi-alert' : 'mdi-check-circle'"></ix-icon>
</span>
<span class="label">
{{ '{tasks, plural, =1 {# send task} other {# send tasks}}' | translate : { tasks: tile.totalSend } }}
{{ '{tasks, plural, =1 {# send task} other {# send tasks} }' | translate : { tasks: tile.totalSend } }}
</span>
</li>
<li>
<span [class]="['icon', tile.failedReceive ? 'warn' : 'safe']">
<ix-icon [name]="tile.failedReceive ? 'mdi-alert' : 'mdi-check-circle'"></ix-icon>
</span>
<span class="label">
{{ '{tasks, plural, =1 {# receive task} other {# receive tasks}}' | translate : { tasks: tile.totalReceive } }}
{{ '{tasks, plural, =1 {# receive task} other {# receive tasks} }' | translate : { tasks: tile.totalReceive } }}
</span>
</li>
<li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ <h3 mat-card-title>
@if (checkQuotas) {
@if (!isLoadingQuotas) {
<div class="value">
{{ 'Quotas set for {n, plural, one {# user} other {# users}}' | translate: { n: userQuotas } }}
{{ 'Quotas set for {n, plural, one {# user} other {# users} }' | translate: { n: userQuotas } }}
</div>
} @else {
<ngx-skeleton-loader class="value-placeholder"></ngx-skeleton-loader>
Expand All @@ -149,7 +149,7 @@ <h3 mat-card-title>
@if (checkQuotas) {
@if (!isLoadingQuotas) {
<div class="value">
{{ 'Quotas set for {n, plural, one {# group} other {# groups}}' | translate: { n: groupQuotas } }}
{{ 'Quotas set for {n, plural, one {# group} other {# groups} }' | translate: { n: groupQuotas } }}
</div>
} @else {
<ngx-skeleton-loader class="value-placeholder"></ngx-skeleton-loader>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ <h1 matDialogTitle>{{ 'Delete' | translate }}</h1>
<p>{{ getErrorMessage() }}</p>
} @else {
<p class="result-message">
{{ 'Deleted {n, plural, one {# snapshot} other {# snapshots}}' | translate:
{{ 'Deleted {n, plural, one {# snapshot} other {# snapshots} }' | translate:
{ n: jobSuccess.length} }}
</p>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<div class="disk-overview">
<div class="tile" [class.active]="selectedView() === EnclosureView.Pools">
<div class="primary-number">{{ poolsInfo().length }}</div>
<div class="title">{{ '{n, plural, one {Pool in Enclosure} other {Pools in Enclosure}}' | translate: { n: poolsInfo().length } }}</div>
<div class="title">{{ '{n, plural, one {Pool in Enclosure} other {Pools in Enclosure} }' | translate: { n: poolsInfo().length } }}</div>
<div class="subtitle">
@if (unhealthyPoolsInfo().length === 0) {
{{ 'All pools are online.' | translate }}
Expand All @@ -32,7 +32,7 @@
</div>
<div class="tile" [class.active]="selectedView() === EnclosureView.DiskStatus">
<div class="primary-number">{{ failedDisks().length }}</div>
<div class="title">{{ '{n, plural, one {Failed Disk} other {Failed Disks}}' | translate: { n: failedDisks().length } }}</div>
<div class="title">{{ '{n, plural, one {Failed Disk} other {Failed Disks} }' | translate: { n: failedDisks().length } }}</div>
<div class="subtitle">
@if (failedDisks().length === 0) {
{{ 'All disks healthy.' | translate }}
Expand All @@ -57,7 +57,7 @@
@if (expanders().length) {
<div class="tile" [class.active]="selectedView() === EnclosureView.Expanders">
<div class="primary-number">{{ expanders().length }}</div>
<div class="title">{{ '{n, plural, one {SAS Expander} other {SAS Expanders}}' | translate: { n: expanders().length} }}</div>
<div class="title">{{ '{n, plural, one {SAS Expander} other {SAS Expanders} }' | translate: { n: expanders().length} }}</div>
<div class="subtitle"> {{ 'on this enclosure.' | translate }} </div>
<button
mat-button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
<ix-input
formControlName="cpu"
[label]="'CPU Configuration' | translate"
[hint]="containersHelptext.cpuHint"
></ix-input>

<ix-input
formControlName="memory"
[label]="'Memory Size' | translate"
[format]="formatter.memorySizeFormatting"
[parse]="formatter.memorySizeParsing"
[hint]="containersHelptext.memoryHint"
></ix-input>
</ix-fieldset>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { MatButton } from '@angular/material/button';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { Role } from 'app/enums/role.enum';
import { containersHelptext } from 'app/helptext/virtualization/containers';
import {
InstanceEnvVariablesFormGroup,
UpdateVirtualizationInstance,
Expand Down Expand Up @@ -141,4 +142,6 @@ export class InstanceEditFormComponent {
return env;
}, {});
}

protected readonly containersHelptext = containersHelptext;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,13 @@ <h3 mat-card-title>
</p>
<p>{{ 'Autostart' | translate }}: {{ instance.autostart | yesNo }}</p>
<p>{{ 'Base Image' | translate }}: {{ instance.image.description || '-' }}</p>
<p>{{ 'CPU' | translate }}: {{ instance.cpu || '-' }}</p>
<p>{{ 'Memory' | translate }}: {{ formatter.memorySizeFormatting(instance.memory) || '-' }}</p>
<p>{{ 'CPU' | translate }}: {{ instance.cpu || ('All Host CPUs' | translate) }}</p>
<p>{{ 'Memory' | translate }}: {{ formatter.memorySizeFormatting(instance.memory) || ('Available Host Memory' | translate) }}</p>

@if (instance.environment && (instance.environment | keyvalue)?.length) {
<p>{{ 'Environment' | translate }}:</p>
<ul class="environment-variables">
@for (env of (instance.environment | keyvalue); track env) {
<li>
<strong>{{ env.key }}:</strong> {{ env.value }}
</li>
}
</ul>
<span class="environment-variables" [matTooltip]="environmentVariablesTooltip()">
{{ '{n, plural, one {# Environment Variable} other {# Environment Variables} }' | translate: { n: (instance.environment | keyvalue)?.length } }}
</span>
}
}
</mat-card-content>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@ mat-card-content p {
}

.environment-variables {
margin-top: 0;
cursor: help;
text-decoration: underline;
text-decoration-color: var(--alt-fg1);
text-decoration-style: dotted;
text-underline-offset: 3px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { KeyValuePipe } from '@angular/common';
import { MatButtonHarness } from '@angular/material/button/testing';
import { MatTooltipHarness } from '@angular/material/tooltip/testing';
import { Router } from '@angular/router';
import { createComponentFactory, mockProvider, Spectator } from '@ngneat/spectator/jest';
import { MockComponent } from 'ng-mocks';
import { of } from 'rxjs';
import { fakeSuccessfulJob } from 'app/core/testing/utils/fake-job.utils';
import { mockJob, mockApi } from 'app/core/testing/utils/mock-api.utils';
import { mockAuth } from 'app/core/testing/utils/mock-auth.utils';
import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive';
Expand All @@ -20,7 +23,6 @@ import {
import {
InstanceGeneralInfoComponent,
} from 'app/pages/virtualization/components/all-instances/instance-details/instance-general-info/instance-general-info.component';
import { ErrorHandlerService } from 'app/services/error-handler.service';
import { SlideInService } from 'app/services/slide-in.service';
import { ApiService } from 'app/services/websocket/api.service';

Expand Down Expand Up @@ -63,15 +65,15 @@ describe('InstanceGeneralInfoComponent', () => {
open: jest.fn(),
}),
mockApi([
mockJob('virt.instance.delete'),
mockJob('virt.instance.delete', fakeSuccessfulJob()),
]),
mockProvider(ErrorHandlerService),
mockProvider(DialogService, {
confirm: jest.fn(() => of(true)),
jobDialog: jest.fn(() => of({
jobDialog: jest.fn(() => ({
afterClosed: jest.fn(() => of({})),
})),
}),
mockProvider(Router),
],
});

Expand All @@ -90,29 +92,44 @@ describe('InstanceGeneralInfoComponent', () => {

it('renders details in card', () => {
const chartExtra = spectator.query('mat-card-content').querySelectorAll('p');
expect(chartExtra).toHaveLength(6);
expect(chartExtra).toHaveLength(5);
expect(chartExtra[0]).toHaveText('Status: Running');
expect(chartExtra[1]).toHaveText('Autostart: Yes');
expect(chartExtra[2]).toHaveText('Base Image: Almalinux 8 amd64 (20241030_23:38)');
expect(chartExtra[3]).toHaveText('CPU: 525');
expect(chartExtra[4]).toHaveText('Memory: 125 MiB');
expect(chartExtra[5]).toHaveText('Environment:');
});

it('renders environment variables', () => {
const envContainer = spectator.query('mat-card-content').querySelectorAll('ul li');
expect(envContainer).toHaveLength(2);
expect(envContainer[0]).toHaveText('SAMPLE_ENV: value2');
expect(envContainer[1]).toHaveText('TEST_ENV: value1');
it('renders correct values when CPU or Memory limit is not set', () => {
spectator.setInput('instance', {
...instance,
cpu: null,
memory: null,
});

const chartExtra = spectator.query('mat-card-content').querySelectorAll('p');

expect(chartExtra[3]).toHaveText('CPU: All Host CPUs');
expect(chartExtra[4]).toHaveText('Memory: Available Host Memory');
});

/** Weird bug */
it.skip('deletes instance when "Delete" button is pressed', async () => {
it('renders environment variables a text with tooltip', async () => {
const environmentVariables = spectator.query('.environment-variables');
expect(environmentVariables).toHaveText('2 Environment Variables');

const tooltip = await loader.getHarness(MatTooltipHarness.with({ selector: '.environment-variables' }));
await tooltip.show();
expect(await tooltip.getTooltipText()).toBe('TEST_ENV = value1\nSAMPLE_ENV = value2');
});

it('deletes instance when "Delete" button is pressed and redirects to list root', async () => {
const deleteButton = await loader.getHarness(MatButtonHarness.with({ text: 'Delete' }));
await deleteButton.click();

expect(spectator.inject(DialogService).jobDialog).toHaveBeenCalled();
expect(spectator.inject(ApiService).job).toHaveBeenLastCalledWith('virt.instance.delete', ['demo']);

expect(spectator.inject(Router).navigate).toHaveBeenCalledWith(['/virtualization'], { state: { hideMobileDetails: true } });
});

it('opens edit instance form when Edit is pressed', async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { KeyValuePipe } from '@angular/common';
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import {
ChangeDetectionStrategy, Component, computed, input,
} from '@angular/core';
import { MatButton } from '@angular/material/button';
import {
MatCard, MatCardActions, MatCardContent, MatCardHeader,
MatCardTitle,
} from '@angular/material/card';
import { MatTooltip } from '@angular/material/tooltip';
import { Router } from '@angular/router';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
Expand Down Expand Up @@ -43,6 +46,7 @@ import { ApiService } from 'app/services/websocket/api.service';
KeyValuePipe,
TestDirective,
MapValuePipe,
MatTooltip,
],
})
export class InstanceGeneralInfoComponent {
Expand All @@ -51,6 +55,10 @@ export class InstanceGeneralInfoComponent {
protected readonly Role = Role;
protected readonly virtualizationStatusLabels = virtualizationStatusLabels;

protected readonly environmentVariablesTooltip = computed(() => {
return Object.entries(this.instance().environment).map(([key, value]) => `${key} = ${value}`).join('\n');
});

constructor(
protected formatter: IxFormatterService,
private dialogService: DialogService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class InstanceListComponent {
return {
type: EmptyType.NoPageData,
title: this.translate.instant('No instances'),
message: this.translate.instant('Instances you created will automatically appear here.'),
message: this.translate.instant('Instances you create will automatically appear here.'),
large: true,
};
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,15 @@
<ix-input
formControlName="cpu"
[label]="'CPU Configuration' | translate"
[hint]="containersHelptext.cpuHint"
></ix-input>

<ix-input
formControlName="memory"
[label]="'Memory Size' | translate"
[format]="formatter.memorySizeFormatting"
[parse]="formatter.memorySizeParsing"
[hint]="containersHelptext.memoryHint"
></ix-input>
</ix-fieldset>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
VirtualizationType,
} from 'app/enums/virtualization.enum';
import { mapToOptions } from 'app/helpers/options.helper';
import { containersHelptext } from 'app/helptext/virtualization/containers';
import { Option } from 'app/interfaces/option.interface';
import {
AvailableGpu,
Expand Down Expand Up @@ -300,4 +301,6 @@ export class InstanceWizardComponent implements OnInit {
});
});
}

protected readonly containersHelptext = containersHelptext;
}
2 changes: 1 addition & 1 deletion src/app/services/navigation/navigation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export class NavigationService {
tooltip: T('Containers'),
icon: iconMarker('view_in_ar'),
state: 'virtualization',
isVisible$: this.hasVms$,
isVisible$: this.hasApps$,
},
{
name: T('Apps'),
Expand Down
2 changes: 1 addition & 1 deletion src/app/services/token-last-used.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class TokenLastUsedService {
private tokenLastUsed$ = new BehaviorSubject<string>(this.window.localStorage.getItem('tokenLastUsed'));

/**
* Check if token was used no more than 5 minutes ago (default )
* Check if token was used no more than 5 minutes ago (default)
*/
get isTokenWithinTimeline$(): Observable<boolean> {
return this.tokenLastUsed$.pipe(
Expand Down
Loading

0 comments on commit b70831e

Please sign in to comment.