Skip to content

Commit

Permalink
NAS-132261 / 25.04 / Setup Truenas Connect integration (#11355)
Browse files Browse the repository at this point in the history
  • Loading branch information
gmboknat authored Jan 20, 2025
1 parent 811d9a1 commit 8883779
Show file tree
Hide file tree
Showing 113 changed files with 2,843 additions and 3 deletions.
36 changes: 36 additions & 0 deletions src/app/enums/truenas-connect-status.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { marker as T } from '@biesbjerg/ngx-translate-extract-marker';

export enum TruenasConnectStatus {
Disabled = 'DISABLED',
ClaimTokenMissing = 'CLAIM_TOKEN_MISSING',
RegistrationFinalizationWaiting = 'REGISTRATION_FINALIZATION_WAITING',
RegistrationFinalizationFailed = 'REGISTRATION_FINALIZATION_FAILED',
RegistrationFinalizationTimeout = 'REGISTRATION_FINALIZATION_TIMEOUT',
RegistrationFinalizationSuccess = 'REGISTRATION_FINALIZATION_SUCCESS',
CertGenerationInProgress = 'CERT_GENERATION_IN_PROGRESS',
CertGenerationFailed = 'CERT_GENERATION_FAILED',
CertGenerationSuccess = 'CERT_GENERATION_SUCCESS',
CertConfigurationFailure = 'CERT_CONFIGURATION_FAILURE',
CertRenewalInProgress = 'CERT_RENEWAL_IN_PROGRESS',
CertRenewalFailure = 'CERT_RENEWAL_FAILURE',
CertRenewalSuccess = 'CERT_RENEWAL_SUCCESS',
Configured = 'CONFIGURED',
}

// eslint-disable-next-line @typescript-eslint/naming-convention
export const TruenasConnectStatusReason: Record<TruenasConnectStatus, string> = {
[TruenasConnectStatus.Disabled]: T('TrueNAS Connect is disabled'),
[TruenasConnectStatus.ClaimTokenMissing]: T('Waiting for claim token to be generated'),
[TruenasConnectStatus.RegistrationFinalizationWaiting]: T('Waiting for registration with TrueNAS Connect to complete'),
[TruenasConnectStatus.RegistrationFinalizationFailed]: T('Registration finalization failed'),
[TruenasConnectStatus.RegistrationFinalizationTimeout]: T('Registration finalization timed out'),
[TruenasConnectStatus.RegistrationFinalizationSuccess]: T('Registration finalization successful'),
[TruenasConnectStatus.CertGenerationInProgress]: T('Certificate generation is in progress'),
[TruenasConnectStatus.CertGenerationFailed]: T('Certificate generation failed'),
[TruenasConnectStatus.CertGenerationSuccess]: T('Certificate generation was successful'),
[TruenasConnectStatus.CertConfigurationFailure]: T('Failed to configure certificate in system UI'),
[TruenasConnectStatus.CertRenewalInProgress]: T('Certificate renewal is in progress'),
[TruenasConnectStatus.CertRenewalFailure]: T('Failed to renew certificate'),
[TruenasConnectStatus.CertRenewalSuccess]: T('Certificate renewal was successful'),
[TruenasConnectStatus.Configured]: T('TrueNAS Connect is configured'),
};
1 change: 1 addition & 0 deletions src/app/helptext/topbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const helptextTopbar = {
toggle_collapse: T('Toggle Collapse'),
tc_connect: T('Connecting to TrueCommand'),
tc_status: T('Status of TrueCommand'),
tnc_status: T('Status of Truenas Connect'),
update: T('Update in Progress'),
reboot_info: T('Reboot Required'),
pending_network_changes: T('Pending Network Changes'),
Expand Down
8 changes: 8 additions & 0 deletions src/app/interfaces/api/api-call-directory.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ import {
TrueCommandConfig, TrueCommandUpdateResponse,
UpdateTrueCommand,
} from 'app/interfaces/true-command-config.interface';
import { TruenasConnectConfig, TruenasConnectUpdate } from 'app/interfaces/truenas-connect-config.interface';
import { Tunable } from 'app/interfaces/tunable.interface';
import { GlobalTwoFactorConfig, GlobalTwoFactorConfigUpdate } from 'app/interfaces/two-factor-config.interface';
import { UpsConfig, UpsConfigUpdate } from 'app/interfaces/ups-config.interface';
Expand Down Expand Up @@ -816,6 +817,13 @@ export interface ApiCallDirectory {
'truecommand.config': { params: void; response: TrueCommandConfig };
'truecommand.update': { params: [UpdateTrueCommand]; response: TrueCommandUpdateResponse };

// Truenas Connect
'tn_connect.config': { params: void; response: TruenasConnectConfig };
'tn_connect.ip_choices': { params: void; response: Record<string, string> };
'tn_connect.update': { params: [TruenasConnectUpdate]; response: TruenasConnectConfig };
'tn_connect.generate_claim_token': { params: void; response: string };
'tn_connect.get_registration_uri': { params: void; response: string };

// TrueNAS
'truenas.accept_eula': { params: void; response: void };
'truenas.get_eula': { params: void; response: string };
Expand Down
2 changes: 2 additions & 0 deletions src/app/interfaces/api/api-event-directory.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { PoolScan } from 'app/interfaces/resilver-job.interface';
import { Service } from 'app/interfaces/service.interface';
import { SmartTestProgressUpdate } from 'app/interfaces/smart-test-progress.interface';
import { TrueCommandConfig } from 'app/interfaces/true-command-config.interface';
import { TruenasConnectConfig } from 'app/interfaces/truenas-connect-config.interface';
import { User } from 'app/interfaces/user.interface';
import { VirtualMachine } from 'app/interfaces/virtual-machine.interface';
import { VirtualizationGlobalConfig, VirtualizationInstance, VirtualizationInstanceMetrics } from 'app/interfaces/virtualization.interface';
Expand All @@ -42,6 +43,7 @@ export interface ApiEventDirectory {
'service.query': { response: Service };
'smart.test.progress': { response: SmartTestProgressUpdate };
'system.reboot.info': { response: SystemRebootInfo };
'tn_connect.config': { response: TruenasConnectConfig };
'truecommand.config': { response: TrueCommandConfig };
'user.query': { response: User };
'virt.global.config': { response: VirtualizationGlobalConfig };
Expand Down
25 changes: 25 additions & 0 deletions src/app/interfaces/truenas-connect-config.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { TruenasConnectStatus, TruenasConnectStatusReason } from 'app/enums/truenas-connect-status.enum';

export interface TruenasConnectConfig extends TruenasConnectUpdate {
id: number;
registration_details: {
scopes: string[];
account_id: string;
system_id: string;
account_name: string;
exp: number;
iat: number;
iss: string;
};
status: TruenasConnectStatus;
status_reason: typeof TruenasConnectStatusReason;
certificate: number;
}

export interface TruenasConnectUpdate {
enabled: boolean;
ips: string[];
tnc_base_url: string;
account_service_base_url: string;
leca_service_base_url: string;
}
8 changes: 8 additions & 0 deletions src/app/interfaces/truenas-connect.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { EnclosureModel } from 'app/enums/enclosure-model.enum';

export interface TruenasConnectRegistration {
version: string;
model: EnclosureModel;
token: string;
system_id: string;
}
3 changes: 3 additions & 0 deletions src/app/modules/layout/topbar/topbar.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
</button>

<ix-truecommand-button></ix-truecommand-button>
@if (hasTncConfig()) {
<ix-truenas-connect-button></ix-truenas-connect-button>
}

@if (hasRebootRequiredReasons()) {
<button
Expand Down
12 changes: 11 additions & 1 deletion src/app/modules/layout/topbar/topbar.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AsyncPipe } from '@angular/common';
import {
ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, signal,
ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, OnInit, signal,
} from '@angular/core';
import { MatBadge } from '@angular/material/badge';
import { MatIconButton } from '@angular/material/button';
Expand Down Expand Up @@ -40,6 +40,8 @@ import { UserMenuComponent } from 'app/modules/layout/topbar/user-menu/user-menu
import { TestDirective } from 'app/modules/test-id/test.directive';
import { ThemeService } from 'app/modules/theme/theme.service';
import { TruecommandButtonComponent } from 'app/modules/truecommand/truecommand-button.component';
import { TruenasConnectService } from 'app/modules/truenas-connect/services/truenas-connect.service';
import { TruenasConnectButtonComponent } from 'app/modules/truenas-connect/truenas-connect-button.component';
import { SystemGeneralService } from 'app/services/system-general.service';
import { AppState } from 'app/store';
import { selectIsHaLicensed } from 'app/store/ha-info/ha-info.selectors';
Expand Down Expand Up @@ -76,6 +78,7 @@ import { alertIndicatorPressed, sidenavIndicatorPressed } from 'app/store/topbar
UiSearchDirective,
TestDirective,
TruecommandButtonComponent,
TruenasConnectButtonComponent,
],
})
export class TopbarComponent implements OnInit {
Expand All @@ -91,6 +94,12 @@ export class TopbarComponent implements OnInit {

readonly hasRebootRequiredReasons = signal(false);
readonly shownDialog = signal(false);
readonly hasTncConfig = computed(() => {
const config = this.tnc.config();
return config?.ips?.length && config.tnc_base_url
&& config.account_service_base_url
&& config.leca_service_base_url;
});

readonly alertBadgeCount$ = this.store$.select(selectImportantUnreadAlertsCount);
readonly hasConsoleFooter$ = this.store$.select(selectHasConsoleFooter);
Expand All @@ -104,6 +113,7 @@ export class TopbarComponent implements OnInit {
private store$: Store<AlertSlice>,
private appStore$: Store<AppState>,
private cdr: ChangeDetectorRef,
private tnc: TruenasConnectService,
) {
this.systemGeneralService.updateRunningNoticeSent.pipe(untilDestroyed(this)).subscribe(() => {
this.updateNotificationSent = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<h1 mat-dialog-title class="title">
<ix-icon name="ix-truenas-connect-logo-color"></ix-icon>
<span class="truenas">TrueNAS &nbsp;</span>
<span>CONNECT</span>
</h1>

<mat-dialog-content>
@if(tnc.config().status !== TruenasConnectStatus.Disabled) {
<span class="warning">{{
'In order to update the form, you need to disable the service first'
| translate
}}</span>
}
<form [formGroup]="form">
<ix-input
formControlName="tnc_base_url"
[label]="'TNC Base Url' | translate"
[tooltip]="tooltips.tnc_base_url | translate"
[required]="true"
></ix-input>
<ix-input
formControlName="account_service_base_url"
[label]="'Account Service Base Url' | translate"
[tooltip]="tooltips.account_service_base_url | translate"
[required]="true"
></ix-input>
<ix-input
formControlName="leca_service_base_url"
[label]="'Leca Service Base Url' | translate"
[tooltip]="tooltips.leca_service_base_url | translate"
[required]="true"
></ix-input>
</form>
</mat-dialog-content>

<mat-dialog-actions>
<div>
@if(tnc.config().status !== TruenasConnectStatus.Disabled) {
<button
mat-button
color="warn"
ixTest="tnc-disable"
(click)="disableService()"
>
{{ 'Disable Service' | translate }}
</button>
} @else {
<button
mat-button
ixTest="tnc-enable"
[disabled]="hasChange()"
(click)="enableService()"
>
{{ 'Enable Service' | translate }}
</button>
}
</div>

<div>
@if(tnc.config().status === TruenasConnectStatus.ClaimTokenMissing) {
<button ixTest="tnc-generate-token" mat-button (click)="generateToken()">
{{ 'Generate Token' | translate }}
</button>
}
<button mat-button ixTest="tnc-form-cancel" (click)="cancel()">
{{ 'Cancel' | translate }}
</button>

@if (tnc.config().status ===
TruenasConnectStatus.RegistrationFinalizationWaiting) {
<button ixTest="tnc-connect" mat-button color="primary" (click)="connect()">
{{ 'Connect' | translate }}
</button>
} @else {
<button
mat-button
ixTest="tnc-connect"
color="primary"
[disabled]="!hasChange() || form.invalid"
(click)="save()"
>
{{ 'Save' | translate }}
</button>
}
</div>
</mat-dialog-actions>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
:host ::ng-deep {
mat-dialog-actions {
display: flex;
justify-content: space-between !important;
}
}

.title {
font-size: 32px;
justify-content: center;

ix-icon {
height: 56px;
margin-right: 8px;
width: 56px;
}

.truenas {
color: var(--primary);
}
}

mat-dialog-content {
text-align: center;

h3 {
padding-bottom: 8px;
}
}

.warning {
color: var(--red);
}
Loading

0 comments on commit 8883779

Please sign in to comment.