</div>
</div>
+ <!-- backend_service -->
+ <div *ngIf="serviceForm.controls.service_type.value === 'ingress'"
+ class="form-group row">
+ <label i18n
+ class="cd-col-form-label"
+ [ngClass]="{'required': ['ingress'].includes(serviceForm.controls.service_type.value)}"
+ for="backend_service">Backend Service</label>
+ <div class="cd-col-form-input">
+ <select id="backend_service"
+ name="backend_service"
+ class="form-control custom-select"
+ formControlName="backend_service">
+ <option *ngIf="services === null"
+ [ngValue]="null"
+ i18n>Loading...</option>
+ <option *ngIf="services !== null && services.length === 0"
+ [ngValue]="null"
+ i18n>-- No service available --</option>
+ <option *ngIf="services !== null && services.length > 0"
+ [ngValue]="null"
+ i18n>-- Select an existing RGW service --</option>
+ <option *ngFor="let service of services"
+ [value]="service.service_name">{{ service.service_name }}</option>
+ </select>
+ <span class="invalid-feedback"
+ *ngIf="serviceForm.showError('backend_service', frm, 'required')"
+ i18n>This field is required.</span>
+ </div>
+ </div>
+
<!-- Service id -->
<div class="form-group row">
<label i18n
class="cd-col-form-label"
- [ngClass]="{'required': ['mds', 'rgw', 'nfs', 'iscsi'].includes(serviceForm.controls.service_type.value)}"
+ [ngClass]="{'required': ['mds', 'rgw', 'nfs', 'iscsi', 'ingress'].includes(serviceForm.controls.service_type.value)}"
for="service_id">Id</label>
<div class="cd-col-form-input">
<input id="service_id"
</div>
</ng-container>
- <!-- RGW & iSCSI -->
- <ng-container *ngIf="!serviceForm.controls.unmanaged.value && ['rgw', 'iscsi'].includes(serviceForm.controls.service_type.value)">
+ <!-- Ingress -->
+ <ng-container *ngIf="!serviceForm.controls.unmanaged.value && serviceForm.controls.service_type.value === 'ingress'">
+ <!-- virtual_ip -->
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ [ngClass]="{'required': ['ingress'].includes(serviceForm.controls.service_type.value)}"
+ for="virtual_ip">
+ <span i18n>Virtual IP</span>
+ <cd-helper>
+ <span i18n>The virtual IP address and subnet (in CIDR notation) where the ingress service will be available.</span>
+ </cd-helper>
+ </label>
+ <div class="cd-col-form-input">
+ <input id="virtual_ip"
+ class="form-control"
+ type="text"
+ formControlName="virtual_ip">
+ <span class="invalid-feedback"
+ *ngIf="serviceForm.showError('virtual_ip', frm, 'required')"
+ i18n>This field is required.</span>
+ </div>
+ </div>
+
+ <!-- frontend_port -->
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ [ngClass]="{'required': ['ingress'].includes(serviceForm.controls.service_type.value)}"
+ for="frontend_port">
+ <span i18n>Frontend Port</span>
+ <cd-helper>
+ <span i18n>The port used to access the ingress service.</span>
+ </cd-helper>
+ </label>
+ <div class="cd-col-form-input">
+ <input id="frontend_port"
+ class="form-control"
+ type="number"
+ formControlName="frontend_port"
+ min="1"
+ max="65535">
+ <span class="invalid-feedback"
+ *ngIf="serviceForm.showError('frontend_port', frm, 'pattern')"
+ i18n>The entered value needs to be a number.</span>
+ <span class="invalid-feedback"
+ *ngIf="serviceForm.showError('frontend_port', frm, 'min')"
+ i18n>The value must be at least 1.</span>
+ <span class="invalid-feedback"
+ *ngIf="serviceForm.showError('frontend_port', frm, 'max')"
+ i18n>The value cannot exceed 65535.</span>
+ <span class="invalid-feedback"
+ *ngIf="serviceForm.showError('frontend_port', frm, 'required')"
+ i18n>This field is required.</span>
+ </div>
+ </div>
+
+ <!-- monitor_port -->
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ [ngClass]="{'required': ['ingress'].includes(serviceForm.controls.service_type.value)}"
+ for="monitor_port">
+ <span i18n>Monitor Port</span>
+ <cd-helper>
+ <span i18n>The port used by haproxy for load balancer status.</span>
+ </cd-helper>
+ </label>
+ <div class="cd-col-form-input">
+ <input id="monitor_port"
+ class="form-control"
+ type="number"
+ formControlName="monitor_port"
+ min="1"
+ max="65535">
+ <span class="invalid-feedback"
+ *ngIf="serviceForm.showError('monitor_port', frm, 'pattern')"
+ i18n>The entered value needs to be a number.</span>
+ <span class="invalid-feedback"
+ *ngIf="serviceForm.showError('monitor_port', frm, 'min')"
+ i18n>The value must be at least 1.</span>
+ <span class="invalid-feedback"
+ *ngIf="serviceForm.showError('monitor_port', frm, 'max')"
+ i18n>The value cannot exceed 65535.</span>
+ <span class="invalid-feedback"
+ *ngIf="serviceForm.showError('monitor_port', frm, 'required')"
+ i18n>This field is required.</span>
+ </div>
+ </div>
+ <!-- virtual_interface_networks -->
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ for="virtual_interface_networks">
+ <span i18n>CIDR Networks</span>
+ <cd-helper>
+ <span i18n>A list of networks to identify which network interface to use for the virtual IP address.</span>
+ </cd-helper>
+ </label>
+ <div class="cd-col-form-input">
+ <input id="virtual_interface_networks"
+ class="form-control"
+ type="text"
+ formControlName="virtual_interface_networks">
+ </div>
+ </div>
+ </ng-container>
+ <!-- RGW, Ingress & iSCSI -->
+ <ng-container *ngIf="!serviceForm.controls.unmanaged.value && ['rgw', 'iscsi', 'ingress'].includes(serviceForm.controls.service_type.value)">
<!-- ssl -->
<div class="form-group row">
<div class="cd-col-form-offset">
formHelper.expectError('api_port', 'pattern');
});
});
+
+ describe('should test service ingress', () => {
+ beforeEach(() => {
+ formHelper.setValue('service_type', 'ingress');
+ formHelper.setValue('service_id', 'rgw.foo');
+ formHelper.setValue('backend_service', 'rgw.foo');
+ formHelper.setValue('virtual_ip', '192.168.20.1/24');
+ formHelper.setValue('ssl', false);
+ });
+
+ it('should submit ingress', () => {
+ component.onSubmit();
+ expect(cephServiceService.create).toHaveBeenCalledWith({
+ service_type: 'ingress',
+ placement: {},
+ unmanaged: false,
+ backend_service: 'rgw.foo',
+ service_id: 'rgw.foo',
+ virtual_ip: '192.168.20.1/24',
+ virtual_interface_networks: null,
+ ssl: false
+ });
+ });
+
+ it('should submit valid frontend and monitor port', () => {
+ // min value
+ formHelper.setValue('frontend_port', 1);
+ formHelper.setValue('monitor_port', 1);
+ component.onSubmit();
+ formHelper.expectValid('frontend_port');
+ formHelper.expectValid('monitor_port');
+
+ // max value
+ formHelper.setValue('frontend_port', 65535);
+ formHelper.setValue('monitor_port', 65535);
+ component.onSubmit();
+ formHelper.expectValid('frontend_port');
+ formHelper.expectValid('monitor_port');
+ });
+
+ it('should submit invalid frontend and monitor port', () => {
+ // min
+ formHelper.setValue('frontend_port', 0);
+ formHelper.setValue('monitor_port', 0);
+ component.onSubmit();
+ formHelper.expectError('frontend_port', 'min');
+ formHelper.expectError('monitor_port', 'min');
+
+ // max
+ formHelper.setValue('frontend_port', 65536);
+ formHelper.setValue('monitor_port', 65536);
+ component.onSubmit();
+ formHelper.expectError('frontend_port', 'max');
+ formHelper.expectError('monitor_port', 'max');
+
+ // pattern
+ formHelper.setValue('frontend_port', 'abc');
+ formHelper.setValue('monitor_port', 'abc');
+ component.onSubmit();
+ formHelper.expectError('frontend_port', 'pattern');
+ formHelper.expectError('monitor_port', 'pattern');
+ });
+ });
});
});
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
import { CdValidators } from '~/app/shared/forms/cd-validators';
import { FinishedTask } from '~/app/shared/models/finished-task';
+import { CephServiceSpec } from '~/app/shared/models/service.interface';
import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
@Component({
labelClick = new Subject<string>();
labelFocus = new Subject<string>();
pools: Array<object>;
+ services: Array<CephServiceSpec> = [];
constructor(
public actionLabels: ActionLabelsI18n,
CdValidators.requiredIf({
service_type: 'iscsi'
}),
+ CdValidators.requiredIf({
+ service_type: 'ingress'
+ }),
CdValidators.composeIf(
{
service_type: 'rgw'
})
]
],
- // RGW & iSCSI
+ // Ingress
+ backend_service: [
+ null,
+ [
+ CdValidators.requiredIf({
+ service_type: 'ingress',
+ unmanaged: false
+ })
+ ]
+ ],
+ virtual_ip: [
+ null,
+ [
+ CdValidators.requiredIf({
+ service_type: 'ingress',
+ unmanaged: false
+ })
+ ]
+ ],
+ frontend_port: [null, [CdValidators.number(false), Validators.min(1), Validators.max(65535)]],
+ monitor_port: [null, [CdValidators.number(false), Validators.min(1), Validators.max(65535)]],
+ virtual_interface_networks: [null],
+ // RGW, Ingress & iSCSI
ssl: [false],
ssl_cert: [
'',
this.poolService.getList().subscribe((resp: Array<object>) => {
this.pools = resp;
});
+ this.cephServiceService.list().subscribe((services: CephServiceSpec[]) => {
+ this.services = services.filter((service: any) => service.service_type === 'rgw');
+ });
}
goToListView() {
serviceSpec['ssl_key'] = values['ssl_key'].trim();
}
break;
+ case 'ingress':
+ serviceSpec['backend_service'] = values['backend_service'];
+ if (_.isString(values['virtual_ip']) && !_.isEmpty(values['virtual_ip'])) {
+ serviceSpec['virtual_ip'] = values['virtual_ip'].trim();
+ }
+ if (_.isNumber(values['frontend_port']) && values['frontend_port'] > 0) {
+ serviceSpec['frontend_port'] = values['frontend_port'];
+ }
+ if (_.isNumber(values['monitor_port']) && values['monitor_port'] > 0) {
+ serviceSpec['monitor_port'] = values['monitor_port'];
+ }
+ serviceSpec['ssl'] = values['ssl'];
+ if (values['ssl']) {
+ serviceSpec['ssl_cert'] = values['ssl_cert'].trim();
+ serviceSpec['ssl_key'] = values['ssl_key'].trim();
+ }
+ serviceSpec['virtual_interface_networks'] = values['virtual_interface_networks'];
+ break;
}
}
this.taskWrapperService
backend_service: Optional[str] = None,
frontend_port: Optional[int] = None,
ssl_cert: Optional[str] = None,
+ ssl_key: Optional[str] = None,
ssl_dh_param: Optional[str] = None,
ssl_ciphers: Optional[List[str]] = None,
ssl_options: Optional[List[str]] = None,
virtual_interface_networks: Optional[List[str]] = [],
haproxy_container_image: Optional[str] = None,
keepalived_container_image: Optional[str] = None,
+ unmanaged: bool = False,
+ ssl: bool = False
):
assert service_type == 'ingress'
super(IngressSpec, self).__init__(
self.backend_service = backend_service
self.frontend_port = frontend_port
self.ssl_cert = ssl_cert
+ self.ssl_key = ssl_key
self.ssl_dh_param = ssl_dh_param
self.ssl_ciphers = ssl_ciphers
self.ssl_options = ssl_options
self.virtual_interface_networks = virtual_interface_networks or []
self.haproxy_container_image = haproxy_container_image
self.keepalived_container_image = keepalived_container_image
+ self.unmanaged = unmanaged
+ self.ssl = ssl
def get_port_start(self) -> List[int]:
return [cast(int, self.frontend_port),