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

Feature: Add Cobbler Signatures UI #257

Merged
merged 2 commits into from
Jul 18, 2024
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
12 changes: 9 additions & 3 deletions projects/cobbler-api/src/lib/cobbler-api.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,15 @@ describe('CobblerApiService', () => {
expect(service).toBeFalsy();
});

xit('should execute the background_signature_update action on the Cobbler Server', () => {
service.background_signature_update('');
expect(service).toBeFalsy();
it('should execute the background_signature_update action on the Cobbler Server', () => {
// eslint-disable-next-line max-len
const methodResponse = `<?xml version='1.0'?><methodResponse><params><param><value><string>2022-09-30_195846_Updating Signatures_6c5300d51c224984b4319fb536cc21d0</string></value></param></params></methodResponse>`
const result = "2022-09-30_195846_Updating Signatures_6c5300d51c224984b4319fb536cc21d0"
service.background_signature_update('').subscribe(value => {
expect(value).toEqual(result)
});
const mockRequest = httpTestingController.expectOne('http://localhost/cobbler_api');
mockRequest.flush(methodResponse);
});

it('should execute the get_events action on the Cobbler Server', () => {
Expand Down
47 changes: 43 additions & 4 deletions projects/cobbler-api/src/lib/cobbler-api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
SyncSystemsOptions,
Version
} from './custom-types/misc';
import {DistroSignatures} from "./custom-types/signatures";

// TODO: Investigate on server side to build and receive well known interfaces, not just plain objects.

Expand Down Expand Up @@ -307,8 +308,9 @@ export class CobblerApiService {
}

background_signature_update(token: string): Observable<string> {
const signatureUpdateOptions: XmlRpcStruct = {members: []}
return this.client
.methodCall('background_signature_update', [token])
.methodCall('background_signature_update', [signatureUpdateOptions, token])
.pipe(
map<MethodResponse | MethodFault, string>((data: MethodResponse | MethodFault) => {
if (AngularXmlrpcService.instanceOfMethodResponse(data)) {
Expand Down Expand Up @@ -2128,17 +2130,54 @@ export class CobblerApiService {
);
}

get_signatures(token: string): Observable<object> {
get_signatures(token: string): Observable<DistroSignatures> {
return this.client
.methodCall('get_signatures', [token])
.pipe(
map<MethodResponse | MethodFault, object>((data: MethodResponse | MethodFault) => {
map<MethodResponse | MethodFault, XmlRpcStruct>((data: MethodResponse | MethodFault) => {
if (AngularXmlrpcService.instanceOfMethodResponse(data)) {
return data.value as object;
return data.value as XmlRpcStruct;
} else if (AngularXmlrpcService.instanceOfMethodFault(data)) {
throw new Error('Getting the signatures failed with code "' + data.faultCode + '" and error message "'
+ data.faultString + '"');
}
}),
map<XmlRpcStruct, DistroSignatures>(value => {
let result: DistroSignatures = {breeds: {}}
value.members.forEach(breedStruct => {
const osBreedStruct = breedStruct.value
if (!AngularXmlrpcService.instanceOfXmlRpcStruct(osBreedStruct)) {
throw new Error("Expected to receive XmlRpcStruct!")
}
const osBreedArray = osBreedStruct.members
osBreedArray.forEach(osBreedObject => {
const osBreedName = osBreedObject.name
result.breeds[osBreedName] = {}
const osVersionArray = osBreedObject.value
if (!AngularXmlrpcService.instanceOfXmlRpcStruct(osVersionArray)) {
throw new Error("Expected to receive XmlRpcStruct!")
}
osVersionArray.members.forEach(osVersionStruct => {
const osVersionValueStruct = osVersionStruct.value
const osVersionName = osVersionStruct.name
if (!AngularXmlrpcService.instanceOfXmlRpcStruct(osVersionValueStruct)) {
throw new Error("Expected to receive XmlRpcStruct!")
}
// @ts-ignore - Due to this being dynamically filled
result.breeds[osBreedName][osVersionName] = {}
osVersionValueStruct.members.forEach(osVersionAttribute => {
const attributeName = osVersionAttribute.name
const attributeValue = osVersionAttribute.value
if (AngularXmlrpcService.instanceOfXmlRpcArray(attributeValue)) {
result.breeds[osBreedName][osVersionName][attributeName] = attributeValue.data
} else {
result.breeds[osBreedName][osVersionName][attributeName] = attributeValue
}
})
})
})
})
return result
})
);
}
Expand Down
70 changes: 70 additions & 0 deletions projects/cobbler-api/src/lib/custom-types/signatures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
export interface DistroSignatureOsVersion {
signatures: string[]
version_file: string | null
version_file_regex?: string | null
kernel_arch: string
kernel_arch_regex?: string | null
supported_arches: (
| "arm"
| "ARM64"
| "armhfp"
| "aarch64"
| "i386"
| "i586"
| "ia64"
| "ppc"
| "ppc64"
| "ppc64le"
| "ppc64el"
| "s390"
| "s390x"
| "x86_64"
| "amd64"
| "AMD64"
| "x86"
)[]
supported_repo_breeds: ("rsync" | "rhn" | "yum" | "apt")[]
kernel_file: string
initrd_file: string
isolinux_ok?: boolean
default_autoinstall: string
kernel_options: string
kernel_options_post: string
template_files?: string
boot_files?: string[]
boot_loaders?: {
arm?: ("pxe" | "ipxe" | "grub")[]
armhfp?: ("pxe" | "ipxe" | "grub")[]
aarch64?: ("pxe" | "ipxe" | "grub")[]
i386?: ("pxe" | "ipxe" | "grub")[]
ia64?: ("pxe" | "ipxe" | "grub")[]
ppc?: ("pxe" | "ipxe" | "grub")[]
ppc64?: ("pxe" | "ipxe" | "grub")[]
ppc64le?: ("pxe" | "ipxe" | "grub")[]
s390?: ("pxe" | "ipxe" | "grub")[]
s390x?: ("pxe" | "ipxe" | "grub")[]
x86_64?: ("pxe" | "ipxe" | "grub")[]
[k: string]: unknown
}
}

/**
* The different distributions supported by Cobbler. This schema is valid for Cobbler with major version 3.
*
* Generated via: https://transform.tools/json-schema-to-typescript
*/
export interface DistroSignatures {
breeds: {
/**
* This interface was referenced by `undefined`'s JSON-Schema definition
* via the `patternProperty` "redhat|debian|ubuntu|suse|vmware|freebsd|xen|unix|windows|powerkvm|generic".
*/
[k: string]: {
/**
* This interface was referenced by `undefined`'s JSON-Schema definition
* via the `patternProperty` "^[a-zA-Z0-9]*$".
*/
[k: string]: DistroSignatureOsVersion
}
}
}
1 change: 1 addition & 0 deletions projects/cobbler-api/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export * from './lib/cobbler-api.service';
export * from './lib/lib.config';
export * from './lib/custom-types/items';
export * from './lib/custom-types/settings';
export * from './lib/custom-types/signatures';
export * from './lib/custom-types/misc';
2 changes: 2 additions & 0 deletions projects/cobbler-frontend/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { NotFoundComponent } from './not-found/not-found.component';
import { AuthGuardService } from './services/auth-guard.service';
import { SettingsViewComponent } from './settings/view/settings-view.component';
import { UnauthorizedComponent } from './unauthorized/unauthorized.component';
import {SignaturesComponent} from "./signatures/signatures.component";


export const routes: Routes = [
Expand Down Expand Up @@ -51,6 +52,7 @@ export const routes: Routes = [
{path: 'status', component: StatusComponent, canActivate: [AuthGuardService]},
{path: 'hardlink', component: HardlinkComponent, canActivate: [AuthGuardService]},
{path: 'events', component: AppEventsComponent, canActivate: [AuthGuardService]},
{path: 'signatures', component: SignaturesComponent, canActivate: [AuthGuardService]},
{path: '404', component: NotFoundComponent},
{path: '**', redirectTo: '/404'},
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ <h2 matSubheader>Cobbler</h2>
<b class="symbol">&#8646;</b>
Events</a
>
<a mat-list-item class="nav-link" [routerLink]="['/signatures']">
<b class="symbol">&#8646;</b>
Signatures</a
>
<a
mat-list-item
class="nav-link"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<div class="title-table">
<div class="title-row">
<h1 class="title title-cell-text">Signatures</h1>
<span class="title-cell-button">
<button mat-icon-button (click)="this.updateSignatures()"><mat-icon>refresh</mat-icon></button>
</span>
</div>
</div>

<mat-spinner *ngIf="isLoading" style="margin:0 auto;"></mat-spinner>
<ng-container *ngIf="!isLoading">
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl">
<!-- This is the tree node template for leaf nodes -->
<mat-tree-node *matTreeNodeDef="let node" matTreeNodePadding>
<!-- use a disabled button to provide padding for tree leaf -->
<button mat-icon-button disabled></button>
{{ node.data }}
</mat-tree-node>
<!-- This is the tree node template for expandable nodes -->
<mat-tree-node *matTreeNodeDef="let node;when: hasChild" matTreeNodePadding>
<button mat-icon-button matTreeNodeToggle
[attr.aria-label]="'Toggle ' + node.data">
<mat-icon class="mat-icon-rtl-mirror">
{{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}
</mat-icon>
</button>
{{ node.data }}
</mat-tree-node>
<!-- This is the tree node template for expandable nodes with an OS version as data -->
<mat-tree-node *matTreeNodeDef="let node;when: hasOsVersion" matTreeNodePadding>
<button mat-icon-button disabled></button>
<table mat-table [dataSource]="node.data" class="mat-elevation-z8">
@for (column of columns; track column) {
<ng-container [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef>
{{ column.header }}
</th>
<td mat-cell *matCellDef="let row">
{{ column.cell(row) }}
</td>
</ng-container>
}
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</mat-tree-node>
</mat-tree>
</ng-container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.title-table {
display: table;
width: 100%;
}

.title-row {
display: table-cell;
width: 100%;
}

.title-cell-text {
display: table-cell;
width: 100%;
vertical-align: middle;
}

.title-cell-button {
display: table-cell;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {provideHttpClient} from '@angular/common/http';
import {provideHttpClientTesting} from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import {MatDividerModule} from '@angular/material/divider';
import {MatIconModule} from '@angular/material/icon';
import {MatTableModule} from '@angular/material/table';
import {MatTreeModule} from '@angular/material/tree';
import {COBBLER_URL} from 'cobbler-api';

import { SignaturesComponent } from './signatures.component';

describe('SignaturesComponent', () => {
let component: SignaturesComponent;
let fixture: ComponentFixture<SignaturesComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
SignaturesComponent,
MatTreeModule,
MatIconModule,
MatTableModule,
MatDividerModule,
],
providers: [
provideHttpClient(),
provideHttpClientTesting(),
{
provide: COBBLER_URL,
useValue: new URL('http://localhost/cobbler_api')
},
]
})
.compileComponents();

fixture = TestBed.createComponent(SignaturesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Loading
Loading