diff --git a/api/v1/webspherelibertyapplication_types.go b/api/v1/webspherelibertyapplication_types.go index a3eb7551..1f7e6469 100644 --- a/api/v1/webspherelibertyapplication_types.go +++ b/api/v1/webspherelibertyapplication_types.go @@ -567,6 +567,15 @@ type WebSphereLibertyApplicationSemeruCloudCompiler struct { // Resource requests and limits for the Semeru Cloud Compiler. The CPU defaults to 100m with a limit of 2000m. The memory defaults to 800Mi, with a limit of 1200Mi. // +operator-sdk:csv:customresourcedefinitions:order=54,type=spec,displayName="Resource Requirements",xDescriptors="urn:alm:descriptor:com.tectonic.ui:resourceRequirements" Resources *corev1.ResourceRequirements `json:"resources,omitempty"` + // The health settings for the Semeru Cloud Compiler. + // +operator-sdk:csv:customresourcedefinitions:order=55,type=spec,displayName="Health" + Health *WebSphereLibertyApplicationSemeruCloudCompilerHealth `json:"health,omitempty"` +} + +type WebSphereLibertyApplicationSemeruCloudCompilerHealth struct { + // The health port for the Semeru Cloud Compiler. Defaults to 38600. + // +operator-sdk:csv:customresourcedefinitions:order=60,type=spec,displayName="Port",xDescriptors="urn:alm:descriptor:com.tectonic.ui:number" + Port *int32 `json:"port,omitempty"` } // Defines SemeruCompiler status @@ -1373,6 +1382,19 @@ func (scc *WebSphereLibertyApplicationSemeruCloudCompiler) GetReplicas() *int32 return &one } +// GetHealth returns the Semeru Cloud Compiler Health configuration +func (scc *WebSphereLibertyApplicationSemeruCloudCompiler) GetHealth() *WebSphereLibertyApplicationSemeruCloudCompilerHealth { + return scc.Health +} + +func (scch *WebSphereLibertyApplicationSemeruCloudCompilerHealth) GetPort() *int32 { + if scch.Port != nil { + return scch.Port + } + defaultPort := int32(38600) + return &defaultPort +} + // GetTopologySpreadConstraints returns the pod topology spread constraints configuration func (cr *WebSphereLibertyApplication) GetTopologySpreadConstraints() common.BaseComponentTopologySpreadConstraints { if cr.Spec.TopologySpreadConstraints == nil { diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 6d58e270..cae94df3 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -707,6 +707,11 @@ func (in *WebSphereLibertyApplicationSemeruCloudCompiler) DeepCopyInto(out *WebS *out = new(corev1.ResourceRequirements) (*in).DeepCopyInto(*out) } + if in.Health != nil { + in, out := &in.Health, &out.Health + *out = new(WebSphereLibertyApplicationSemeruCloudCompilerHealth) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebSphereLibertyApplicationSemeruCloudCompiler. @@ -719,6 +724,26 @@ func (in *WebSphereLibertyApplicationSemeruCloudCompiler) DeepCopy() *WebSphereL return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebSphereLibertyApplicationSemeruCloudCompilerHealth) DeepCopyInto(out *WebSphereLibertyApplicationSemeruCloudCompilerHealth) { + *out = *in + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebSphereLibertyApplicationSemeruCloudCompilerHealth. +func (in *WebSphereLibertyApplicationSemeruCloudCompilerHealth) DeepCopy() *WebSphereLibertyApplicationSemeruCloudCompilerHealth { + if in == nil { + return nil + } + out := new(WebSphereLibertyApplicationSemeruCloudCompilerHealth) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WebSphereLibertyApplicationService) DeepCopyInto(out *WebSphereLibertyApplicationService) { *out = *in diff --git a/bundle/manifests/ibm-websphere-liberty.clusterserviceversion.yaml b/bundle/manifests/ibm-websphere-liberty.clusterserviceversion.yaml index 35759a40..e2ce17b3 100644 --- a/bundle/manifests/ibm-websphere-liberty.clusterserviceversion.yaml +++ b/bundle/manifests/ibm-websphere-liberty.clusterserviceversion.yaml @@ -67,7 +67,7 @@ metadata: capabilities: Auto Pilot categories: Application Runtime containerImage: icr.io/cpopen/websphere-liberty-operator:daily - createdAt: "2025-12-03T04:52:57Z" + createdAt: "2026-01-21T21:18:59Z" description: Deploy and manage containerized Liberty applications features.operators.openshift.io/disconnected: "true" olm.skipRange: '>=1.0.0 <1.5.2' @@ -607,6 +607,14 @@ spec: path: semeruCloudCompiler.resources x-descriptors: - urn:alm:descriptor:com.tectonic.ui:resourceRequirements + - description: The health settings for the Semeru Cloud Compiler. + displayName: Health + path: semeruCloudCompiler.health + - description: The health port for the Semeru Cloud Compiler. Defaults to 38600. + displayName: Port + path: semeruCloudCompiler.health.port + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:number - description: 'Product edition. Defaults to IBM WebSphere Application Server. Other options: IBM WebSphere Application Server Liberty Core, IBM WebSphere Application Server Network Deployment' diff --git a/bundle/manifests/liberty.websphere.ibm.com_webspherelibertyapplications.yaml b/bundle/manifests/liberty.websphere.ibm.com_webspherelibertyapplications.yaml index f58e99f4..8716f25b 100644 --- a/bundle/manifests/liberty.websphere.ibm.com_webspherelibertyapplications.yaml +++ b/bundle/manifests/liberty.websphere.ibm.com_webspherelibertyapplications.yaml @@ -5228,6 +5228,15 @@ spec: enable: description: Enable the Semeru Cloud Compiler. Defaults to false. type: boolean + health: + description: The health settings for the Semeru Cloud Compiler. + properties: + port: + description: The health port for the Semeru Cloud Compiler. + Defaults to 38600. + format: int32 + type: integer + type: object replicas: description: Number of desired pods for the Semeru Cloud Compiler. Defaults to 1. diff --git a/config/crd/bases/liberty.websphere.ibm.com_webspherelibertyapplications.yaml b/config/crd/bases/liberty.websphere.ibm.com_webspherelibertyapplications.yaml index 434ae40f..9e551da8 100644 --- a/config/crd/bases/liberty.websphere.ibm.com_webspherelibertyapplications.yaml +++ b/config/crd/bases/liberty.websphere.ibm.com_webspherelibertyapplications.yaml @@ -5224,6 +5224,15 @@ spec: enable: description: Enable the Semeru Cloud Compiler. Defaults to false. type: boolean + health: + description: The health settings for the Semeru Cloud Compiler. + properties: + port: + description: The health port for the Semeru Cloud Compiler. + Defaults to 38600. + format: int32 + type: integer + type: object replicas: description: Number of desired pods for the Semeru Cloud Compiler. Defaults to 1. diff --git a/config/manifests/bases/ibm-websphere-liberty.clusterserviceversion.yaml b/config/manifests/bases/ibm-websphere-liberty.clusterserviceversion.yaml index c99b2283..e4bd1dd1 100644 --- a/config/manifests/bases/ibm-websphere-liberty.clusterserviceversion.yaml +++ b/config/manifests/bases/ibm-websphere-liberty.clusterserviceversion.yaml @@ -543,6 +543,14 @@ spec: path: semeruCloudCompiler.resources x-descriptors: - urn:alm:descriptor:com.tectonic.ui:resourceRequirements + - description: The health settings for the Semeru Cloud Compiler. + displayName: Health + path: semeruCloudCompiler.health + - description: The health port for the Semeru Cloud Compiler. Defaults to 38600. + displayName: Port + path: semeruCloudCompiler.health.port + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:number - description: 'Product edition. Defaults to IBM WebSphere Application Server. Other options: IBM WebSphere Application Server Liberty Core, IBM WebSphere Application Server Network Deployment' diff --git a/internal/controller/semeru_compiler.go b/internal/controller/semeru_compiler.go index 795c0590..d8e19cae 100644 --- a/internal/controller/semeru_compiler.go +++ b/internal/controller/semeru_compiler.go @@ -46,14 +46,65 @@ const ( SemeruGenerationLabelNameSuffix = "/semeru-compiler-generation" StatusReferenceSemeruGeneration = "semeruGeneration" StatusReferenceSemeruInstancesCompleted = "semeruInstancesCompleted" + SemeruContainerName = "compiler" ) -// Create the Deployment and Service objects for a Semeru Compiler used by a Websphere Liberty Application -func (r *ReconcileWebSphereLiberty) reconcileSemeruCompiler(wlva *wlv1.WebSphereLibertyApplication) (error, string, bool) { - compilerMeta := metav1.ObjectMeta{ +func getCompilerMeta(wlva *wlv1.WebSphereLibertyApplication) metav1.ObjectMeta { + return metav1.ObjectMeta{ Name: getSemeruCompilerNameWithGeneration(wlva), Namespace: wlva.GetNamespace(), } +} + +func getSemeruDeploymentContainer(deploy *appsv1.Deployment) (corev1.Container, error) { + for _, container := range deploy.Spec.Template.Spec.Containers { + if container.Name == SemeruContainerName { + return container, nil + } + } + return corev1.Container{}, fmt.Errorf("could not find the Semeru Deployment container") +} + +// Returns true if the semeru health port configuration has changed otherwise false +func (r *ReconcileWebSphereLiberty) upgradeSemeruHealthPorts(inputMeta metav1.ObjectMeta, wlva *wlv1.WebSphereLibertyApplication) bool { + var healthPort int32 = 38600 + if wlva.GetSemeruCloudCompiler().GetHealth() != nil { + healthPort = *wlva.GetSemeruCloudCompiler().GetHealth().GetPort() + } + semeruDeployment := &appsv1.Deployment{ObjectMeta: inputMeta} + if r.GetClient().Get(context.TODO(), types.NamespacedName{Name: semeruDeployment.Name, Namespace: semeruDeployment.Namespace}, semeruDeployment) == nil { + container, err := getSemeruDeploymentContainer(semeruDeployment) + if err != nil { + return false + } + if healthPort == 38400 && len(container.Ports) > 1 { + return true + } + containsHealthPort := false + for _, port := range container.Ports { + if port.ContainerPort == healthPort { + containsHealthPort = true + } + } + if !containsHealthPort { + return true + } + } + return false +} + +// Create the Deployment and Service objects for a Semeru Compiler used by a Websphere Liberty Application +func (r *ReconcileWebSphereLiberty) reconcileSemeruCompiler(wlva *wlv1.WebSphereLibertyApplication) (error, string, bool) { + compilerMeta := getCompilerMeta(wlva) + + // check for any diffs that require generation changes + if r.isSemeruEnabled(wlva) { + upgradeRequired := r.upgradeSemeruHealthPorts(compilerMeta, wlva) + if upgradeRequired { + createNewSemeruGeneration(wlva) // update generation + compilerMeta = getCompilerMeta(wlva) // adjust compilerMeta to reference the new generation + } + } currentGeneration := getGeneration(wlva) @@ -255,6 +306,8 @@ func (r *ReconcileWebSphereLiberty) deleteCompletedSemeruInstances(wlva *wlv1.We } func (r *ReconcileWebSphereLiberty) reconcileSemeruDeployment(wlva *wlv1.WebSphereLibertyApplication, deploy *appsv1.Deployment) { + var port int32 = 38400 + var healthPort int32 = 38600 deploy.Labels = getLabels(wlva) deploy.Spec.Strategy.Type = appsv1.RecreateDeploymentStrategyType @@ -276,11 +329,20 @@ func (r *ReconcileWebSphereLiberty) reconcileSemeruDeployment(wlva *wlv1.WebSphe limitsMemory := getQuantityFromLimitsOrDefault(instanceResources, corev1.ResourceMemory, "1200Mi") limitsCPU := getQuantityFromLimitsOrDefault(instanceResources, corev1.ResourceCPU, "2000m") + if semeruCloudCompiler.GetHealth() != nil { + healthPort = *semeruCloudCompiler.GetHealth().GetPort() + } + var portIntOrStr intstr.IntOrString + if healthPort == port { + portIntOrStr = intstr.FromInt32(port) + } else { + portIntOrStr = intstr.FromString(fmt.Sprintf("%d-tcp", healthPort)) + } // Liveness probe livenessProbe := corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ TCPSocket: &corev1.TCPSocketAction{ - Port: intstr.FromInt(38400), + Port: portIntOrStr, }, }, InitialDelaySeconds: 10, @@ -291,7 +353,7 @@ func (r *ReconcileWebSphereLiberty) reconcileSemeruDeployment(wlva *wlv1.WebSphe readinessProbe := corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ TCPSocket: &corev1.TCPSocketAction{ - Port: intstr.FromInt(38400), + Port: portIntOrStr, }, }, InitialDelaySeconds: 5, @@ -301,6 +363,22 @@ func (r *ReconcileWebSphereLiberty) reconcileSemeruDeployment(wlva *wlv1.WebSphe semeruPodMatchLabels := map[string]string{ "app.kubernetes.io/instance": getSemeruCompilerNameWithGeneration(wlva), } + containerPorts := make([]corev1.ContainerPort, 0) + containerPorts = append(containerPorts, corev1.ContainerPort{ + ContainerPort: port, + Protocol: corev1.ProtocolTCP, + }) + + healthProbesFlag := "" + if healthPort != port { + healthProbesFlag = " -XX:+JITServerHealthProbes" + fmt.Sprintf(" -XX:JITServerHealthProbePort=%d", healthPort) + containerPorts[0].Name = fmt.Sprintf("%d-tcp", port) + containerPorts = append(containerPorts, corev1.ContainerPort{ + Name: fmt.Sprintf("%d-tcp", healthPort), + ContainerPort: healthPort, + Protocol: corev1.ProtocolTCP, + }) + } deploy.Spec.Template = corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: getLabels(wlva), @@ -333,16 +411,11 @@ func (r *ReconcileWebSphereLiberty) reconcileSemeruDeployment(wlva *wlv1.WebSphe }, Containers: []corev1.Container{ { - Name: "compiler", + Name: SemeruContainerName, Image: wlva.Status.GetImageReference(), ImagePullPolicy: *wlva.GetPullPolicy(), Command: []string{"jitserver"}, - Ports: []corev1.ContainerPort{ - { - ContainerPort: 38400, - Protocol: corev1.ProtocolTCP, - }, - }, + Ports: containerPorts, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceMemory: requestsMemory, @@ -356,6 +429,7 @@ func (r *ReconcileWebSphereLiberty) reconcileSemeruDeployment(wlva *wlv1.WebSphe Env: []corev1.EnvVar{ {Name: "OPENJ9_JAVA_OPTIONS", Value: "-XX:+JITServerLogConnections" + " -XX:+JITServerShareROMClasses" + + healthProbesFlag + " -XX:JITServerSSLKey=/etc/x509/certs/tls.key" + " -XX:JITServerSSLCert=/etc/x509/certs/tls.crt"}, }, @@ -413,16 +487,34 @@ func (r *ReconcileWebSphereLiberty) reconcileSemeruDeployment(wlva *wlv1.WebSphe func reconcileSemeruService(svc *corev1.Service, wlva *wlv1.WebSphereLibertyApplication) { var port int32 = 38400 + var healthPort int32 = 38600 var timeout int32 = 86400 svc.Labels = getLabels(wlva) svc.Spec.Selector = getSelectors(wlva) utils.CustomizeServiceAnnotations(svc) - if len(svc.Spec.Ports) == 0 { + numPorts := len(svc.Spec.Ports) + if numPorts == 0 { svc.Spec.Ports = append(svc.Spec.Ports, corev1.ServicePort{}) } + svc.Spec.Ports[0].Protocol = corev1.ProtocolTCP svc.Spec.Ports[0].Port = port svc.Spec.Ports[0].TargetPort = intstr.FromInt(int(port)) + if wlva.GetSemeruCloudCompiler().GetHealth() != nil { + healthPort = *wlva.GetSemeruCloudCompiler().GetHealth().GetPort() + } + if healthPort != port { + numPorts = len(svc.Spec.Ports) + if numPorts == 1 { + svc.Spec.Ports = append(svc.Spec.Ports, corev1.ServicePort{}) + } + svc.Spec.Ports[0].Name = fmt.Sprintf("%d-tcp", port) + svc.Spec.Ports[0].TargetPort = intstr.FromString(fmt.Sprintf("%d-tcp", port)) + svc.Spec.Ports[1].Name = fmt.Sprintf("%d-tcp", healthPort) + svc.Spec.Ports[1].Protocol = corev1.ProtocolTCP + svc.Spec.Ports[1].Port = healthPort + svc.Spec.Ports[1].TargetPort = intstr.FromString(fmt.Sprintf("%d-tcp", healthPort)) + } svc.Spec.SessionAffinity = corev1.ServiceAffinityClientIP svc.Spec.SessionAffinityConfig = &corev1.SessionAffinityConfig{ ClientIP: &corev1.ClientIPConfig{ @@ -585,14 +677,13 @@ func (r *ReconcileWebSphereLiberty) getSemeruJavaOptions(instance *wlv1.WebSpher certificateLocation = "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" } jitServerAddress := instance.Status.SemeruCompiler.ServiceHostname - jitSeverOptions := fmt.Sprintf("-XX:+UseJITServer -XX:+JITServerLogConnections -XX:JITServerAddress=%v -XX:JITServerSSLRootCerts=%v", - jitServerAddress, certificateLocation) + jitServerOptions := fmt.Sprintf("-XX:+UseJITServer -XX:+JITServerLogConnections -XX:JITServerAddress=%v -XX:JITServerSSLRootCerts=%v", jitServerAddress, certificateLocation) args := []string{ "/bin/bash", "-c", - "export OPENJ9_JAVA_OPTIONS=\"$OPENJ9_JAVA_OPTIONS " + jitSeverOptions + - "\" && export OPENJ9_RESTORE_JAVA_OPTIONS=\"$OPENJ9_RESTORE_JAVA_OPTIONS " + jitSeverOptions + + "export OPENJ9_JAVA_OPTIONS=\"$OPENJ9_JAVA_OPTIONS " + jitServerOptions + + "\" && export OPENJ9_RESTORE_JAVA_OPTIONS=\"$OPENJ9_RESTORE_JAVA_OPTIONS " + jitServerOptions + "\" && server run", } return args diff --git a/internal/deploy/kubectl/websphereliberty-app-crd.yaml b/internal/deploy/kubectl/websphereliberty-app-crd.yaml index bebf6299..8061ffbc 100644 --- a/internal/deploy/kubectl/websphereliberty-app-crd.yaml +++ b/internal/deploy/kubectl/websphereliberty-app-crd.yaml @@ -5227,6 +5227,15 @@ spec: enable: description: Enable the Semeru Cloud Compiler. Defaults to false. type: boolean + health: + description: The health settings for the Semeru Cloud Compiler. + properties: + port: + description: The health port for the Semeru Cloud Compiler. + Defaults to 38600. + format: int32 + type: integer + type: object replicas: description: Number of desired pods for the Semeru Cloud Compiler. Defaults to 1. diff --git a/internal/deploy/kustomize/daily/base/websphere-liberty-crd.yaml b/internal/deploy/kustomize/daily/base/websphere-liberty-crd.yaml index bebf6299..8061ffbc 100644 --- a/internal/deploy/kustomize/daily/base/websphere-liberty-crd.yaml +++ b/internal/deploy/kustomize/daily/base/websphere-liberty-crd.yaml @@ -5227,6 +5227,15 @@ spec: enable: description: Enable the Semeru Cloud Compiler. Defaults to false. type: boolean + health: + description: The health settings for the Semeru Cloud Compiler. + properties: + port: + description: The health port for the Semeru Cloud Compiler. + Defaults to 38600. + format: int32 + type: integer + type: object replicas: description: Number of desired pods for the Semeru Cloud Compiler. Defaults to 1.