From: Avan Thakkar Date: Tue, 20 Apr 2021 12:19:23 +0000 (+0530) Subject: mgr/dashboard: fix HAProxy (now called ingress) X-Git-Tag: v16.2.5~51^2~3^2~17 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=f3e956c7fe1b7c543d988f9f5fca55a534d03b45;p=ceph.git mgr/dashboard: fix HAProxy (now called ingress) Fixes: https://tracker.ceph.com/issues/50319 Signed-off-by: Avan Thakkar Support from Dashboard UI to create Ingress service type. (cherry picked from commit 21318e8fa965c352e00d84c04ee072dd9fe45e4f) --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.html index e2d4f01140cce..4774299b78d93 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.html @@ -29,11 +29,41 @@ + +
+ +
+ + This field is required. +
+
+
- - + + + +
+ +
+ + This field is required. +
+
+ + +
+ +
+ + The entered value needs to be a number. + The value must be at least 1. + The value cannot exceed 65535. + This field is required. +
+
+ + +
+ +
+ + The entered value needs to be a number. + The value must be at least 1. + The value cannot exceed 65535. + This field is required. +
+
+ +
+ +
+ +
+
+
+ +
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.spec.ts index 6ed8b414532ab..5b7fc6cac43b0 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.spec.ts @@ -331,5 +331,68 @@ describe('ServiceFormComponent', () => { 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'); + }); + }); }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.ts index 533f2ae833a75..aee978849a7b6 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.ts @@ -18,6 +18,7 @@ import { CdFormBuilder } from '~/app/shared/forms/cd-form-builder'; 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({ @@ -38,6 +39,7 @@ export class ServiceFormComponent extends CdForm implements OnInit { labelClick = new Subject(); labelFocus = new Subject(); pools: Array; + services: Array = []; constructor( public actionLabels: ActionLabelsI18n, @@ -76,6 +78,9 @@ export class ServiceFormComponent extends CdForm implements OnInit { CdValidators.requiredIf({ service_type: 'iscsi' }), + CdValidators.requiredIf({ + service_type: 'ingress' + }), CdValidators.composeIf( { service_type: 'rgw' @@ -147,7 +152,29 @@ export class ServiceFormComponent extends CdForm implements OnInit { }) ] ], - // 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: [ '', @@ -218,6 +245,9 @@ export class ServiceFormComponent extends CdForm implements OnInit { this.poolService.getList().subscribe((resp: Array) => { this.pools = resp; }); + this.cephServiceService.list().subscribe((services: CephServiceSpec[]) => { + this.services = services.filter((service: any) => service.service_type === 'rgw'); + }); } goToListView() { @@ -313,6 +343,24 @@ export class ServiceFormComponent extends CdForm implements OnInit { 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 diff --git a/src/python-common/ceph/deployment/service_spec.py b/src/python-common/ceph/deployment/service_spec.py index 5a21071649c3d..3e2915dbb0cbb 100644 --- a/src/python-common/ceph/deployment/service_spec.py +++ b/src/python-common/ceph/deployment/service_spec.py @@ -879,6 +879,7 @@ class IngressSpec(ServiceSpec): 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, @@ -891,6 +892,8 @@ class IngressSpec(ServiceSpec): 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__( @@ -901,6 +904,7 @@ class IngressSpec(ServiceSpec): 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 @@ -912,6 +916,8 @@ class IngressSpec(ServiceSpec): 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),