Skip to content

Commit

Permalink
NAS-133478 / 25.04 / Allow ISOs to be uploaded in VM wizard (#11350)
Browse files Browse the repository at this point in the history
  • Loading branch information
bvasilenko authored Jan 20, 2025
1 parent 8883779 commit 362cac6
Show file tree
Hide file tree
Showing 93 changed files with 546 additions and 37 deletions.
1 change: 1 addition & 0 deletions src/app/interfaces/api/api-job-directory.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ export interface ApiJobDirectory {
response: VirtualizationInstance;
};

'virt.volume.import_iso': { params: [{ name: string }]; response: { name: string } };
'virt.global.update': { params: [VirtualizationGlobalConfigUpdate]; response: VirtualizationGlobalConfig };

// VM
Expand Down
1 change: 1 addition & 0 deletions src/app/interfaces/virtualization.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export interface VirtualizationDisk {
source: string | null;
destination: string | null;
product_id: string;
boot_priority?: number;
}

export interface VirtualizationGpu {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,46 @@
[showLabels]="true"
></ix-icon-group>

<div class="image-field">
<ix-input
class="input"
formControlName="image"
[readonly]="true"
[label]="'Image' | translate"
@if (isVm()) {
<ix-radio-group
formControlName="image_type"
[label]="'VM Image Options' | translate"
[required]="true"
></ix-input>
[options]="imageOptions$"
></ix-radio-group>
}

<button
mat-button
ixTest="browse-images"
type="button"
(click)="onBrowseImages()"
>{{ 'Browse Catalog' | translate }}</button>
</div>
@if (form.value.image_type === SelectImageType.Choose) {
<div class="image-field">
<ix-input
class="input"
formControlName="image"
[readonly]="true"
[label]="'Image' | translate"
[required]="true"
></ix-input>

<button
mat-button
ixTest="browse-images"
type="button"
(click)="onBrowseImages()"
>{{ 'Browse Catalog' | translate }}</button>
</div>
} @else {
<ix-file-input
formControlName="image_file"
acceptedFiles=".iso"
[label]="'Image' | translate"
[multiple]="false"
[required]="true"
></ix-file-input>
<ix-input
formControlName="image_file_name"
[label]="'Image Name' | translate"
[required]="true"
></ix-input>
}
</ix-form-section>

<ix-form-section
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import {
} from '@ngneat/spectator/jest';
import { MockComponent } from 'ng-mocks';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { GiB } from 'app/constants/bytes.constant';
import { fakeFile } from 'app/core/testing/utils/fake-file.uitls';
import { fakeSuccessfulJob } from 'app/core/testing/utils/fake-job.utils';
import { mockCall, mockJob, mockApi } from 'app/core/testing/utils/mock-api.utils';
import { mockAuth } from 'app/core/testing/utils/mock-auth.utils';
import {
VirtualizationDeviceType,
VirtualizationNicType,
Expand All @@ -23,7 +24,6 @@ import {
} from 'app/enums/virtualization.enum';
import { Job } from 'app/interfaces/job.interface';
import { VirtualizationInstance } from 'app/interfaces/virtualization.interface';
import { AuthService } from 'app/modules/auth/auth.service';
import { DialogService } from 'app/modules/dialog/dialog.service';
import { IxCheckboxHarness } from 'app/modules/forms/ix-forms/components/ix-checkbox/ix-checkbox.harness';
import { IxIconGroupHarness } from 'app/modules/forms/ix-forms/components/ix-icon-group/ix-icon-group.harness';
Expand All @@ -35,6 +35,7 @@ import { ApiService } from 'app/modules/websocket/api.service';
import { InstanceWizardComponent } from 'app/pages/virtualization/components/instance-wizard/instance-wizard.component';
import { VirtualizationImageWithId } from 'app/pages/virtualization/components/instance-wizard/select-image-dialog/select-image-dialog.component';
import { FilesystemService } from 'app/services/filesystem.service';
import { UploadService } from 'app/services/upload.service';

describe('InstanceWizardComponent', () => {
let spectator: SpectatorRouting<InstanceWizardComponent>;
Expand All @@ -47,7 +48,10 @@ describe('InstanceWizardComponent', () => {
MockComponent(PageHeaderComponent),
],
providers: [
mockProvider(AuthService, { hasRole: () => of(true) }),
mockAuth(),
mockProvider(UploadService, {
uploadAsJob: jest.fn(() => of(fakeSuccessfulJob())),
}),
mockProvider(Router),
mockProvider(FilesystemService),
mockApi([
Expand Down Expand Up @@ -89,14 +93,13 @@ describe('InstanceWizardComponent', () => {
manufacturer: 'Linux 6.6.44-production+truenas xhci-hcd',
},
}),
mockJob('virt.volume.import_iso', fakeSuccessfulJob({ name: 'image.iso' })),
mockJob('virt.instance.create', fakeSuccessfulJob({ id: 'new' } as VirtualizationInstance)),
]),
mockProvider(SnackbarService),
mockProvider(DialogService, {
jobDialog: jest.fn((request$: Observable<Job>) => ({
afterClosed: () => request$.pipe(
map((job) => job.result),
),
afterClosed: () => request$,
})),
}),
mockProvider(MatDialog, {
Expand Down Expand Up @@ -336,6 +339,54 @@ describe('InstanceWizardComponent', () => {
expect(spectator.inject(SnackbarService).success).toHaveBeenCalled();
});

it('loads image and creates new instance when form is submitted', async () => {
global.Date.now = jest.fn(() => (new Date('2025-01-20 12:00:00')).getTime());

const instanceType = await loader.getHarness(IxIconGroupHarness.with({ label: 'Virtualization Method' }));
await instanceType.setValue('VM');

await form.fillForm({
Name: 'new',
'VM Image Options': 'Upload an ISO image',
'CPU Configuration': '2',
'Memory Size': '1 GiB',
});

const fakeImage = fakeFile('image.iso');
await form.fillForm({ Image: [fakeImage] });

const createButton = await loader.getHarness(MatButtonHarness.with({ text: 'Create' }));
await createButton.click();

expect(spectator.inject(UploadService).uploadAsJob).toHaveBeenCalledWith({
file: fakeImage,
method: 'virt.volume.import_iso',
params: [{
name: 'image_1737367200000.iso',
upload_iso: true,
}],
});

expect(spectator.inject(ApiService).job).toHaveBeenCalledWith('virt.instance.create', [{
name: 'new',
autostart: true,
cpu: '2',
instance_type: VirtualizationType.Vm,
devices: [{
dev_type: VirtualizationDeviceType.Disk,
source: 'image_1737367200000.iso',
destination: null,
boot_priority: 1,
}],
enable_vnc: false,
source_type: null,
memory: 1073741824,
vnc_port: null,
}]);
expect(spectator.inject(DialogService).jobDialog).toHaveBeenCalled();
expect(spectator.inject(SnackbarService).success).toHaveBeenCalled();
});

it('sends no NIC devices when default network settings checkbox is set', async () => {
await form.fillForm({
Name: 'new',
Expand Down
Loading

0 comments on commit 362cac6

Please sign in to comment.