* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* openFuyao is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package controller
import (
"fmt"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/rand"
ctrl "sigs.k8s.io/controller-runtime"
serverlessdbv1 "serverlessdb/api/v1"
)
func extractPgPort(podTemplate corev1.PodTemplateSpec) int32 {
for _, container := range podTemplate.Spec.Containers {
if container.Name != serverlessdbv1.ContainerNameCompute {
continue
}
if port := findPgPort(container.Ports); port > 0 {
return port
}
}
return serverlessdbv1.DefaultPgPort
}
func findPgPort(ports []corev1.ContainerPort) int32 {
for _, port := range ports {
if port.Name == serverlessdbv1.PortNamePg {
return port.ContainerPort
}
}
return 0
}
func generateInstanceName(poolName string) string {
return fmt.Sprintf("%s-%s", poolName, rand.String(5))
}
func buildPod(pool *serverlessdbv1.DBResourcePool, instance *serverlessdbv1.DBInstance, scheme *runtime.Scheme) (*corev1.Pod, error) {
podSpec := pool.Spec.PodTemplate.Spec.DeepCopy()
labels := map[string]string{}
for k, v := range pool.Spec.PodTemplate.ObjectMeta.Labels {
labels[k] = v
}
labels[serverlessdbv1.LabelKeyDBResourcePool] = pool.Name
if appRef := instance.Labels[serverlessdbv1.LabelKeyAppRef]; appRef != "" {
labels[serverlessdbv1.LabelKeyAppRef] = appRef
}
annotations := map[string]string{}
for k, v := range pool.Spec.PodTemplate.ObjectMeta.Annotations {
annotations[k] = v
}
pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: instance.Name,
Namespace: pool.Namespace,
Labels: labels,
Annotations: annotations,
},
Spec: *podSpec,
}
applyCurrentResources(pod, instance.Status.CurrentResources)
if err := ctrlSetControllerReference(instance, pod, scheme); err != nil {
return nil, err
}
return pod, nil
}
func applyCurrentResources(pod *corev1.Pod, res []serverlessdbv1.ContainerResources) {
if len(res) == 0 {
return
}
for i := range pod.Spec.Containers {
c := &pod.Spec.Containers[i]
var matched *serverlessdbv1.ContainerResources
for j := range res {
if res[j].Name == c.Name {
matched = &res[j]
break
}
}
if matched == nil {
continue
}
if c.Resources.Requests == nil {
c.Resources.Requests = corev1.ResourceList{}
}
if c.Resources.Limits == nil {
c.Resources.Limits = corev1.ResourceList{}
}
if q, ok := parseQuantity(matched.Resources.Cpu); ok {
c.Resources.Requests[corev1.ResourceCPU] = q
}
if q, ok := parseQuantity(matched.Resources.Memory); ok {
c.Resources.Requests[corev1.ResourceMemory] = q
}
if q, ok := parseQuantity(matched.Resources.CpuLimit); ok {
c.Resources.Limits[corev1.ResourceCPU] = q
}
if q, ok := parseQuantity(matched.Resources.MemoryLimit); ok {
c.Resources.Limits[corev1.ResourceMemory] = q
}
}
}
func parseQuantity(s string) (resource.Quantity, bool) {
if s == "" {
return resource.Quantity{}, false
}
q, err := resource.ParseQuantity(s)
if err != nil {
return resource.Quantity{}, false
}
return q, true
}
func ctrlSetControllerReference(owner, controlled metav1.Object, scheme *runtime.Scheme) error {
return ctrl.SetControllerReference(owner, controlled, scheme)
}