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

[release/v2.21] Configure OpenStack ingress cluster settings #5865

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
4 changes: 4 additions & 0 deletions src/app/node-data/basic/provider/openstack/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ export class OpenstackBasicNodeDataComponent extends BaseFormValidator implement
return this.form.get(Controls.UseFloatingIP).disabled;
}

isDiskSizeRequired(): boolean {
return this.form.get(Controls.CustomDiskSize).hasValidator(Validators.required);
}

onFlavorChange(flavor: string): void {
this._nodeDataService.nodeData.spec.cloud.openstack.flavor = flavor;
this._nodeDataService.nodeDataChanges.next(this._nodeDataService.nodeData);
Expand Down
1 change: 1 addition & 0 deletions src/app/node-data/basic/provider/openstack/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
[formControlName]="Controls.DiskSize"
mode="errors"
label="Disk Size in GB"
[required]="isDiskSizeRequired()"
hint="An additional network storage volume will be created and used as the disk for the VM."
min="10"
max="16000">
Expand Down
7 changes: 7 additions & 0 deletions src/app/shared/components/cluster-summary/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,13 @@ <h4>IPv6</h4>
<div key>IPv6 Subnet Pool</div>
<div value>{{cluster.spec.cloud?.openstack.ipv6SubnetPool}}</div>
</km-property>
<km-property-boolean label="Enable Ingress Hostname"
[value]="cluster.spec.cloud?.openstack.enableIngressHostname">
</km-property-boolean>
<km-property *ngIf="cluster.spec.cloud?.openstack.ingressHostnameSuffix">
<div key>Ingress Hostname Suffix</div>
<div value>{{cluster.spec.cloud?.openstack.ingressHostnameSuffix}}</div>
</km-property>
</ng-container>

<!-- Equinix -->
Expand Down
11 changes: 10 additions & 1 deletion src/app/shared/entity/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,9 +259,18 @@ export class OpenstackCloudSpec extends ExtraCloudSpecOptions {
subnetID: string;
ipv6SubnetID: string;
ipv6SubnetPool: string;
enableIngressHostname?: boolean;
ingressHostnameSuffix?: string;

static isEmpty(spec: OpenstackCloudSpec): boolean {
return _.difference(Object.keys(spec), Object.keys(ExtraCloudSpecOptions.new(spec))).every(key => !spec[key]);
return _.difference(
OpenstackCloudSpec.getKeysToCompare(spec),
OpenstackCloudSpec.getKeysToCompare(ExtraCloudSpecOptions.new(spec))
).every(key => !spec[key]);
}

private static getKeysToCompare(spec: ExtraCloudSpecOptions): string[] {
return Object.keys(spec).filter(key => key !== 'enableIngressHostname' && key !== 'ingressHostnameSuffix');
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ export class OpenstackProviderExtendedAppCredentialsComponent
ipv6SubnetPoolsLabel = IPv6SubnetPoolState.Empty;
isDualStackNetworkSelected: boolean;

private _applicationCredentialID = '';
private _applicationCredentialSecret = '';

constructor(
private readonly _builder: FormBuilder,
private readonly _presets: PresetsService,
Expand Down Expand Up @@ -148,11 +151,24 @@ export class OpenstackProviderExtendedAppCredentialsComponent
}

ngAfterViewInit(): void {
merge(this._clusterSpecService.clusterChanges, this._credentialsTypeService.credentialsTypeChanges)
.pipe(filter(_ => this._clusterSpecService.provider === NodeProvider.OPENSTACK))
.pipe(debounceTime(this._debounceTime))
.pipe(
tap(_ => {
if (!this._hasApplicationCredentials()) {
this._clearCredentials();
}
})
)
.subscribe(null);

merge(this._clusterSpecService.clusterChanges, this._credentialsTypeService.credentialsTypeChanges)
.pipe(filter(_ => this._clusterSpecService.provider === NodeProvider.OPENSTACK))
.pipe(debounceTime(this._debounceTime))
.pipe(tap(_ => (!this._hasApplicationCredentials() ? this._clearSecurityGroup() : null)))
.pipe(filter(_ => this._hasApplicationCredentials()))
.pipe(filter(_ => this._areCredentialsChanged()))
.pipe(switchMap(_ => this._securityGroupListObservable()))
.pipe(takeUntil(this._unsubscribe))
.subscribe(this._loadSecurityGroups.bind(this));
Expand All @@ -162,6 +178,7 @@ export class OpenstackProviderExtendedAppCredentialsComponent
.pipe(debounceTime(this._debounceTime))
.pipe(tap(_ => (!this._hasApplicationCredentials() ? this._clearNetwork() : null)))
.pipe(filter(_ => this._hasApplicationCredentials()))
.pipe(filter(_ => this._areCredentialsChanged()))
.pipe(switchMap(_ => this._networkListObservable()))
.pipe(takeUntil(this._unsubscribe))
.subscribe(this._loadNetworks.bind(this));
Expand All @@ -182,6 +199,7 @@ export class OpenstackProviderExtendedAppCredentialsComponent
.pipe(filter(_ => Cluster.isDualStackNetworkSelected(this._clusterSpecService.cluster)))
.pipe(tap(_ => (!this._hasApplicationCredentials() ? this._clearSubnetPool() : null)))
.pipe(filter(_ => this._hasApplicationCredentials()))
.pipe(filter(_ => this._areCredentialsChanged()))
.pipe(switchMap(_ => this._subnetPoolListObservable()))
.pipe(takeUntil(this._unsubscribe))
.subscribe(this._loadSubnetPools.bind(this));
Expand Down Expand Up @@ -270,6 +288,33 @@ export class OpenstackProviderExtendedAppCredentialsComponent
);
}

private _areCredentialsChanged(): boolean {
let credentialsChanged = false;

if (
this._clusterSpecService.cluster.spec.cloud.openstack?.applicationCredentialID !== this._applicationCredentialID
) {
this._applicationCredentialID = this._clusterSpecService.cluster.spec.cloud.openstack.applicationCredentialID;
credentialsChanged = true;
}

if (
this._clusterSpecService.cluster.spec.cloud.openstack?.applicationCredentialSecret !==
this._applicationCredentialSecret
) {
this._applicationCredentialSecret =
this._clusterSpecService.cluster.spec.cloud.openstack.applicationCredentialSecret;
credentialsChanged = true;
}

return credentialsChanged;
}

private _clearCredentials(): void {
this._applicationCredentialID = '';
this._applicationCredentialSecret = '';
}

private _canLoadSubnet(): boolean {
return this._hasApplicationCredentials() && !!this._clusterSpecService.cluster.spec.cloud.openstack.network;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@ import {
CredentialsType,
OpenstackCredentialsTypeService,
} from '@app/wizard/step/provider-settings/provider/extended/openstack/service';
import {ClusterSpecService} from '@core/services/cluster-spec';
import {CloudSpec, Cluster, ClusterSpec, OpenstackCloudSpec} from '@shared/entity/cluster';
import {BaseFormValidator} from '@shared/validators/base-form.validator';
import {merge} from 'rxjs';
import {distinctUntilChanged, takeUntil} from 'rxjs/operators';

enum Controls {
Credentials = 'credentials',
EnableIngressHostname = 'enableIngressHostname',
IngressHostnameSuffix = 'ingressHostnameSuffix',
}

@Component({
Expand Down Expand Up @@ -50,6 +56,7 @@ export class OpenstackProviderExtendedComponent extends BaseFormValidator implem

constructor(
private readonly _builder: FormBuilder,
private readonly _clusterSpecService: ClusterSpecService,
private readonly _credentialsTypeService: OpenstackCredentialsTypeService
) {
super('Openstack Provider Extended');
Expand All @@ -58,11 +65,52 @@ export class OpenstackProviderExtendedComponent extends BaseFormValidator implem
ngOnInit(): void {
this.form = this._builder.group({
[Controls.Credentials]: this._builder.control(''),
[Controls.EnableIngressHostname]: this._builder.control(false),
[Controls.IngressHostnameSuffix]: this._builder.control({value: '', disabled: true}),
});

this.form
.get(Controls.EnableIngressHostname)
.valueChanges.pipe(takeUntil(this._unsubscribe))
.subscribe(value => {
this._enable(value, Controls.IngressHostnameSuffix);
});

merge(
this.form.get(Controls.EnableIngressHostname).valueChanges,
this.form.get(Controls.IngressHostnameSuffix).valueChanges
)
.pipe(distinctUntilChanged())
.pipe(takeUntil(this._unsubscribe))
.subscribe(_ => (this._clusterSpecService.cluster = this._getClusterEntity()));
}

ngOnDestroy(): void {
this._unsubscribe.next();
this._unsubscribe.complete();
}

private _getClusterEntity(): Cluster {
return {
spec: {
cloud: {
openstack: {
enableIngressHostname: this.form.get(Controls.EnableIngressHostname).value || null,
ingressHostnameSuffix: this.form.get(Controls.IngressHostnameSuffix).value || null,
} as OpenstackCloudSpec,
} as CloudSpec,
} as ClusterSpec,
} as Cluster;
}

private _enable(enable: boolean, name: string): void {
if (enable && this.form.get(name).disabled) {
this.form.get(name).enable();
}

if (!enable && this.form.get(name).enabled) {
this.form.get(name).reset();
this.form.get(name).disable();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ export class OpenstackProviderExtendedDefaultCredentialsComponent
ipv6SubnetPoolsLabel = IPv6SubnetPoolState.Empty;
isDualStackNetworkSelected: boolean;

private _domain = '';
private _username = '';
private _password = '';
private _project = '';
private _projectID = '';

constructor(
private readonly _builder: FormBuilder,
private readonly _presets: PresetsService,
Expand Down Expand Up @@ -148,11 +154,24 @@ export class OpenstackProviderExtendedDefaultCredentialsComponent
}

ngAfterViewInit(): void {
merge(this._clusterSpecService.clusterChanges, this._credentialsTypeService.credentialsTypeChanges)
.pipe(filter(_ => this._clusterSpecService.provider === NodeProvider.OPENSTACK))
.pipe(debounceTime(this._debounceTime))
.pipe(
tap(_ => {
if (!this._hasRequiredCredentials()) {
this._clearCredentials();
}
})
)
.subscribe(null);

merge(this._clusterSpecService.clusterChanges, this._credentialsTypeService.credentialsTypeChanges)
.pipe(filter(_ => this._clusterSpecService.provider === NodeProvider.OPENSTACK))
.pipe(debounceTime(this._debounceTime))
.pipe(tap(_ => (!this._hasRequiredCredentials() ? this._clearSecurityGroup() : null)))
.pipe(filter(_ => this._hasRequiredCredentials()))
.pipe(filter(_ => this._areCredentialsChanged()))
.pipe(switchMap(_ => this._securityGroupListObservable()))
.pipe(takeUntil(this._unsubscribe))
.subscribe(this._loadSecurityGroups.bind(this));
Expand All @@ -162,6 +181,7 @@ export class OpenstackProviderExtendedDefaultCredentialsComponent
.pipe(debounceTime(this._debounceTime))
.pipe(tap(_ => (!this._hasRequiredCredentials() ? this._clearNetwork() : null)))
.pipe(filter(_ => this._hasRequiredCredentials()))
.pipe(filter(_ => this._areCredentialsChanged()))
.pipe(switchMap(_ => this._networkListObservable()))
.pipe(takeUntil(this._unsubscribe))
.subscribe(this._loadNetworks.bind(this));
Expand All @@ -182,6 +202,7 @@ export class OpenstackProviderExtendedDefaultCredentialsComponent
.pipe(filter(_ => Cluster.isDualStackNetworkSelected(this._clusterSpecService.cluster)))
.pipe(tap(_ => (!this._hasRequiredCredentials() ? this._clearNetwork() : null)))
.pipe(filter(_ => this._hasRequiredCredentials()))
.pipe(filter(_ => this._areCredentialsChanged()))
.pipe(switchMap(_ => this._subnetPoolListObservable()))
.pipe(takeUntil(this._unsubscribe))
.subscribe(this._loadSubnetPools.bind(this));
Expand Down Expand Up @@ -282,6 +303,45 @@ export class OpenstackProviderExtendedDefaultCredentialsComponent
return this._hasBasicCredentials() && this._hasProject();
}

private _areCredentialsChanged(): boolean {
let credentialsChanged = false;

if (this._clusterSpecService.cluster.spec.cloud.openstack?.domain !== this._domain) {
this._domain = this._clusterSpecService.cluster.spec.cloud.openstack.domain;
credentialsChanged = true;
}

if (this._clusterSpecService.cluster.spec.cloud.openstack?.username !== this._username) {
this._username = this._clusterSpecService.cluster.spec.cloud.openstack.username;
credentialsChanged = true;
}

if (this._clusterSpecService.cluster.spec.cloud.openstack?.password !== this._password) {
this._password = this._clusterSpecService.cluster.spec.cloud.openstack.password;
credentialsChanged = true;
}

if (this._clusterSpecService.cluster.spec.cloud.openstack?.project !== this._project) {
this._project = this._clusterSpecService.cluster.spec.cloud.openstack.project;
credentialsChanged = true;
}

if (this._clusterSpecService.cluster.spec.cloud.openstack?.projectID !== this._projectID) {
this._projectID = this._clusterSpecService.cluster.spec.cloud.openstack.projectID;
credentialsChanged = true;
}

return credentialsChanged;
}

private _clearCredentials(): void {
this._domain = '';
this._username = '';
this._password = '';
this._project = '';
this._projectID = '';
}

private _canLoadSubnet(): boolean {
return this._hasBasicCredentials() && !!this._clusterSpecService.cluster.spec.cloud.openstack.network;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,36 @@

<form [formGroup]="form"
fxLayout="column"
fxLayoutGap="8px">
fxLayoutGap="15px">
<ng-container *ngIf="credentialsType === CredentialsType.Default">
<km-wizard-openstack-provider-extended-default-credentials [formControl]="form.get(Controls.Credentials)"></km-wizard-openstack-provider-extended-default-credentials>
</ng-container>

<ng-container *ngIf="credentialsType === CredentialsType.Application">
<km-wizard-openstack-provider-extended-app-credentials [formControl]="form.get(Controls.Credentials)"></km-wizard-openstack-provider-extended-app-credentials>
</ng-container>

<mat-checkbox [formControlName]="Controls.EnableIngressHostname"
[name]="Controls.EnableIngressHostname">
<div fxLayoutGap="8px"
fxLayoutAlign=" center">
<span>Enable Ingress Hostname</span>
<i class="km-pointer km-icon-info"
matTooltip="Enable the &quot;enable-ingress-hostname&quot; cloud provider option on the Openstack CCM."></i>
</div>
</mat-checkbox>

<mat-form-field fxFlex>
<mat-label>Ingress Hostname Suffix</mat-label>
<input matInput
[formControlName]="Controls.IngressHostnameSuffix"
[name]="Controls.IngressHostnameSuffix"
type="text"
autocomplete="off">
<mat-hint>
Set a specific suffix for the hostnames used for the PROXY protocol
workaround that is enabled by <code>EnableIngressHostname</code>. The suffix
is set to <code>nip.io</code> by default.
</mat-hint>
</mat-form-field>
</form>