package handler
import (
"context"
"encoding/json"
"fmt"
"github.com/goodrain/rainbond/api/model"
dbmodel "github.com/goodrain/rainbond/db/model"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"path"
"sigs.k8s.io/yaml"
"strconv"
"strings"
)
func PodTemplateSpecResource(parameter model.YamlResourceParameter, volumeClaimTemplate []corev1.PersistentVolumeClaim, clientset *kubernetes.Clientset) {
logrus.Infof("into function PodTemplateSpecResource")
var ps []model.PortManagement
NameAndPort := make(map[string]int32)
var po int32
for _, port := range parameter.Template.Spec.Containers[0].Ports {
NameAndPort[port.Name] = port.ContainerPort
switch string(port.Protocol) {
case "UDP", "udp":
po = port.ContainerPort
ps = append(ps, model.PortManagement{
Name: port.Name,
Port: port.ContainerPort,
Protocol: "udp",
Inner: false,
Outer: false,
})
case "HTTP", "http":
po = port.ContainerPort
ps = append(ps, model.PortManagement{
Name: port.Name,
Port: port.ContainerPort,
Protocol: "http",
Inner: false,
Outer: false,
})
default:
po = port.ContainerPort
ps = append(ps, model.PortManagement{
Name: port.Name,
Port: port.ContainerPort,
Protocol: "tcp",
Inner: false,
Outer: false,
})
}
logrus.Warningf("Transport protocol type not recognized%v", port.Protocol)
}
var envs []model.ENVManagement
for i := 0; i < len(parameter.Template.Spec.Containers[0].Env); i++ {
if cm := parameter.Template.Spec.Containers[0].Env[i].ValueFrom; cm == nil {
envs = append(envs, model.ENVManagement{
ENVKey: parameter.Template.Spec.Containers[0].Env[i].Name,
ENVValue: parameter.Template.Spec.Containers[0].Env[i].Value,
ENVExplain: "",
})
parameter.Template.Spec.Containers[0].Env = append(parameter.Template.Spec.Containers[0].Env[:i], parameter.Template.Spec.Containers[0].Env[i+1:]...)
}
}
var configs []model.ConfigManagement
cmMap := make(map[string]corev1.ConfigMap)
cmList, err := clientset.CoreV1().ConfigMaps(parameter.Namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get ConfigMap%v", err)
}
cmList.Items = append(cmList.Items, parameter.CMs...)
for _, cm := range cmList.Items {
cmMap[cm.Name] = cm
}
var volumeAttributes []corev1.Volume
volumeMountAttributes := parameter.Template.Spec.Containers[0].VolumeMounts
for _, volume := range parameter.Template.Spec.Volumes {
if volume.ConfigMap != nil && err == nil {
cm, _ := cmMap[volume.ConfigMap.Name]
cmData := cm.Data
isLog := true
for i, volumeMount := range volumeMountAttributes {
if volume.Name != volumeMount.Name {
continue
}
volumeMountAttributes = append(volumeMountAttributes[:i], volumeMountAttributes[i+1:]...)
if len(volumeMountAttributes) == 0 {
volumeMountAttributes = nil
}
isLog = false
if volume.ConfigMap.Items != nil {
if volumeMount.SubPath != "" {
configName := ""
var itemMode int32
for _, item := range volume.ConfigMap.Items {
if item.Path == volumeMount.SubPath {
configName = item.Key
if item.Mode != nil {
itemMode = *item.Mode
}
break
}
}
mode8 := strconv.FormatInt(int64(itemMode), 8)
mode, err := strconv.ParseInt(mode8, 10, 32)
if err != nil || mode == 0 {
mode = 755
}
configs = append(configs, model.ConfigManagement{
ConfigName: configName,
ConfigPath: volumeMount.MountPath,
ConfigValue: cmData[configName],
Mode: int32(mode),
})
} else {
p := volumeMount.MountPath
for _, item := range volume.ConfigMap.Items {
p := path.Join(p, item.Path)
var itemMode int32
if item.Mode != nil {
itemMode = *item.Mode
}
mode8 := strconv.FormatInt(int64(itemMode), 8)
mode, err := strconv.ParseInt(mode8, 10, 32)
if err != nil || mode == 0 {
mode = 755
}
configs = append(configs, model.ConfigManagement{
ConfigName: item.Key,
ConfigPath: p,
ConfigValue: cmData[item.Key],
Mode: int32(mode),
})
}
}
} else {
var mode10 int32
if volume.ConfigMap.DefaultMode != nil {
mode10 = *volume.ConfigMap.DefaultMode
}
mode8 := strconv.FormatInt(int64(mode10), 8)
mode, err := strconv.ParseInt(mode8, 10, 32)
if err != nil || mode == 0 {
mode = 755
}
if volumeMount.SubPath != "" {
configs = append(configs, model.ConfigManagement{
ConfigName: volumeMount.SubPath,
ConfigPath: volumeMount.MountPath,
ConfigValue: cmData[volumeMount.SubPath],
Mode: int32(mode),
})
} else {
mountPath := volumeMount.MountPath
for key, val := range cmData {
volumeMountPath := path.Join(mountPath, key)
configs = append(configs, model.ConfigManagement{
ConfigName: key,
ConfigPath: volumeMountPath,
ConfigValue: val,
Mode: int32(mode),
})
}
}
}
break
}
if isLog {
volumeAttributes = append(volumeAttributes, volume)
logrus.Warningf("configmap type resource %v is not mounted in volumemount", volume.ConfigMap.Name)
}
continue
}
volumeAttributes = append(volumeAttributes, volume)
}
HPAList, err := clientset.AutoscalingV1().HorizontalPodAutoscalers(parameter.Namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("failed to get HorizontalPodAutoscalers list:%v", err)
}
HPAList.Items = append(HPAList.Items, parameter.HPAs...)
var t model.TelescopicManagement
for _, hpa := range HPAList.Items {
if (hpa.Spec.ScaleTargetRef.Kind != model.Deployment && hpa.Spec.ScaleTargetRef.Kind != model.StateFulSet) || hpa.Spec.ScaleTargetRef.Name != parameter.Name {
t.Enable = false
continue
}
t.Enable = true
t.MinReplicas = *hpa.Spec.MinReplicas
t.MaxReplicas = hpa.Spec.MaxReplicas
var cpuormemorys []*dbmodel.TenantServiceAutoscalerRuleMetrics
cpuUsage := hpa.Spec.TargetCPUUtilizationPercentage
if cpuUsage != nil {
cpuormemorys = append(cpuormemorys, &dbmodel.TenantServiceAutoscalerRuleMetrics{
MetricsType: "resource_metrics",
MetricsName: "cpu",
MetricTargetType: "utilization",
MetricTargetValue: int(*cpuUsage),
})
}
CPUAndMemoryJSON, ok := hpa.Annotations["autoscaling.alpha.kubernetes.io/metrics"]
if ok {
type com struct {
T string `json:"type"`
Resource map[string]interface{}
}
var c []com
err := json.Unmarshal([]byte(CPUAndMemoryJSON), &c)
if err != nil {
logrus.Errorf("autoscaling.alpha.kubernetes.io/metrics parsing failed:%v", err)
}
for _, cpuormemory := range c {
switch cpuormemory.Resource["name"] {
case "cpu":
cpu := fmt.Sprint(cpuormemory.Resource["targetAverageValue"])
cpuUnit := cpu[len(cpu)-1:]
var cpuUsage int
if cpuUnit == "m" {
cpuUsage, _ = strconv.Atoi(cpu[:len(cpu)-1])
}
if cpuUnit == "g" || cpuUnit == "G" {
cpuUsage, _ = strconv.Atoi(cpu[:len(cpu)-1])
cpuUsage = cpuUsage * 1024
}
cpuormemorys = append(cpuormemorys, &dbmodel.TenantServiceAutoscalerRuleMetrics{
MetricsType: "resource_metrics",
MetricsName: "cpu",
MetricTargetType: "average_value",
MetricTargetValue: cpuUsage,
})
case "memory":
memory := fmt.Sprint(cpuormemory.Resource["targetAverageValue"])
memoryUnit := memory[:len(memory)-1]
var MemoryUsage int
if memoryUnit == "m" {
MemoryUsage, _ = strconv.Atoi(memory[:len(memory)-1])
}
if memoryUnit == "g" || memoryUnit == "G" {
MemoryUsage, _ = strconv.Atoi(memory[:len(memory)-1])
MemoryUsage = MemoryUsage * 1024
}
cpuormemorys = append(cpuormemorys, &dbmodel.TenantServiceAutoscalerRuleMetrics{
MetricsType: "resource_metrics",
MetricsName: "cpu",
MetricTargetType: "average_value",
MetricTargetValue: MemoryUsage,
})
}
}
}
t.CPUOrMemory = cpuormemorys
}
var hcm model.HealthyCheckManagement
livenessProbe := parameter.Template.Spec.Containers[0].LivenessProbe
if livenessProbe != nil {
var httpHeaders []string
if livenessProbe.HTTPGet != nil {
for _, httpHeader := range livenessProbe.HTTPGet.HTTPHeaders {
nv := httpHeader.Name + "=" + httpHeader.Value
httpHeaders = append(httpHeaders, nv)
}
hcm.DetectionMethod = strings.ToLower(string(livenessProbe.HTTPGet.Scheme))
if hcm.DetectionMethod == "" {
hcm.DetectionMethod = "tcp"
if len(httpHeaders) > 0 {
hcm.DetectionMethod = "http"
}
}
hcm.Path = livenessProbe.HTTPGet.Path
hcm.Port = int(livenessProbe.HTTPGet.Port.IntVal)
if hcm.Port == 0 {
hcm.Port = int(NameAndPort[livenessProbe.HTTPGet.Port.StrVal])
}
}
if hcm.Port == 0 {
hcm.Port = int(po)
}
hcm.Status = 1
if livenessProbe.Exec != nil {
hcm.Command = strings.Join(livenessProbe.Exec.Command, " ")
}
hcm.HTTPHeader = strings.Join(httpHeaders, ",")
hcm.Mode = "liveness"
hcm.InitialDelaySecond = int(livenessProbe.InitialDelaySeconds)
hcm.PeriodSecond = int(livenessProbe.PeriodSeconds)
hcm.TimeoutSecond = int(livenessProbe.TimeoutSeconds)
hcm.FailureThreshold = int(livenessProbe.FailureThreshold)
hcm.SuccessThreshold = int(livenessProbe.SuccessThreshold)
} else {
readinessProbe := parameter.Template.Spec.Containers[0].ReadinessProbe
if readinessProbe != nil {
var httpHeaders []string
if readinessProbe.HTTPGet != nil {
for _, httpHeader := range readinessProbe.HTTPGet.HTTPHeaders {
nv := httpHeader.Name + "=" + httpHeader.Value
httpHeaders = append(httpHeaders, nv)
}
hcm.DetectionMethod = strings.ToLower(string(readinessProbe.HTTPGet.Scheme))
if hcm.DetectionMethod == "" {
hcm.DetectionMethod = "tcp"
if len(httpHeaders) > 0 {
hcm.DetectionMethod = "http"
}
}
hcm.Path = readinessProbe.HTTPGet.Path
hcm.Port = int(readinessProbe.HTTPGet.Port.IntVal)
if hcm.Port == 0 {
hcm.Port = int(NameAndPort[livenessProbe.HTTPGet.Port.StrVal])
}
if hcm.Port == 0 {
hcm.Port = int(po)
}
}
if hcm.Port == 0 {
hcm.Port = int(po)
}
hcm.Status = 1
hcm.Mode = "readiness"
if readinessProbe.Exec != nil {
hcm.Command = strings.Join(readinessProbe.Exec.Command, " ")
}
hcm.HTTPHeader = strings.Join(httpHeaders, ",")
hcm.InitialDelaySecond = int(readinessProbe.InitialDelaySeconds)
hcm.PeriodSecond = int(readinessProbe.PeriodSeconds)
hcm.TimeoutSecond = int(readinessProbe.TimeoutSeconds)
hcm.FailureThreshold = int(readinessProbe.FailureThreshold)
hcm.SuccessThreshold = int(readinessProbe.SuccessThreshold)
}
}
var attributes []*dbmodel.ComponentK8sAttributes
if parameter.Template.Spec.Containers[0].Env != nil && len(parameter.Template.Spec.Containers[0].Env) > 0 {
envYaml, err := ObjectToJSONORYaml("yaml", parameter.Template.Spec.Containers[0].Env)
if err != nil {
logrus.Errorf("pod %v template env transformation yaml failure: %v", parameter.Name, err)
}
envAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameENV,
SaveType: "yaml",
AttributeValue: envYaml,
}
attributes = append(attributes, envAttributes)
}
if volumeAttributes != nil {
volumesYaml, err := ObjectToJSONORYaml("yaml", volumeAttributes)
if err != nil {
logrus.Errorf("pod %v template volume transformation yaml failure: %v", parameter.Name, err)
}
volumesAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameVolumes,
SaveType: "yaml",
AttributeValue: volumesYaml,
}
attributes = append(attributes, volumesAttributes)
}
if volumeMountAttributes != nil {
volumeMountsYaml, err := ObjectToJSONORYaml("yaml", volumeMountAttributes)
if err != nil {
logrus.Errorf("pod %v template volumemount transformation yaml failure: %v", parameter.Name, err)
}
volumeMountsAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameVolumeMounts,
SaveType: "yaml",
AttributeValue: volumeMountsYaml,
}
attributes = append(attributes, volumeMountsAttributes)
}
if parameter.Template.Spec.ServiceAccountName != "" {
serviceAccountAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameServiceAccountName,
SaveType: "string",
AttributeValue: parameter.Template.Spec.ServiceAccountName,
}
attributes = append(attributes, serviceAccountAttributes)
}
if parameter.Template.Labels != nil {
labelsJSON, err := ObjectToJSONORYaml("json", parameter.Template.Labels)
if err != nil {
logrus.Errorf("pod %v template label transformation json failure: %v", parameter.Name, err)
} else {
labelsAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameLabels,
SaveType: "json",
AttributeValue: labelsJSON,
}
attributes = append(attributes, labelsAttributes)
}
}
if parameter.Template.Annotations != nil {
annotationsJSON, err := ObjectToJSONORYaml("json", parameter.Template.Annotations)
if err != nil {
logrus.Errorf("pod %v template annotations transformation json failure: %v", parameter.Name, err)
} else {
labelsAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameAnnotations,
SaveType: "json",
AttributeValue: annotationsJSON,
}
attributes = append(attributes, labelsAttributes)
}
}
if parameter.Template.Spec.NodeSelector != nil {
NodeSelectorJSON, err := ObjectToJSONORYaml("json", parameter.Template.Spec.NodeSelector)
if err != nil {
logrus.Errorf("pod %v template nodeselector transformation json failure: %v", parameter.Name, err)
}
nodeSelectorAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameNodeSelector,
SaveType: "json",
AttributeValue: NodeSelectorJSON,
}
attributes = append(attributes, nodeSelectorAttributes)
}
if volumeClaimTemplate != nil {
volumeClaimTemplateYaml, err := ObjectToJSONORYaml("yaml", volumeClaimTemplate)
if err != nil {
logrus.Errorf("pod %v template volumeClaimTemplate transformation yaml failure: %v", parameter.Name, err)
}
vctAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameVolumeClaimTemplate,
SaveType: "yaml",
AttributeValue: volumeClaimTemplateYaml,
}
attributes = append(attributes, vctAttributes)
}
if parameter.Template.Spec.Containers[0].EnvFrom != nil {
envFromYaml, err := ObjectToJSONORYaml("yaml", parameter.Template.Spec.Containers[0].EnvFrom)
if err != nil {
logrus.Errorf("pod %v template envFrom transformation yaml failure: %v", parameter.Name, err)
} else {
envFromAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameENVFromSource,
SaveType: "yaml",
AttributeValue: envFromYaml,
}
attributes = append(attributes, envFromAttributes)
}
}
if parameter.Template.Spec.Tolerations != nil {
tolerationsYaml, err := ObjectToJSONORYaml("yaml", parameter.Template.Spec.Tolerations)
if err != nil {
logrus.Errorf("pod %v template Tolerations transformation yaml failure: %v", parameter.Name, err)
}
tolerationsAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameTolerations,
SaveType: "yaml",
AttributeValue: tolerationsYaml,
}
attributes = append(attributes, tolerationsAttributes)
}
if parameter.Template.Spec.Affinity != nil {
affinityYaml, err := ObjectToJSONORYaml("yaml", parameter.Template.Spec.Affinity)
if err != nil {
logrus.Errorf("pod %v template Affinity transformation yaml failure: %v", parameter.Name, err)
}
affinityAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameAffinity,
SaveType: "yaml",
AttributeValue: affinityYaml,
}
attributes = append(attributes, affinityAttributes)
}
if securityContext := parameter.Template.Spec.Containers[0].SecurityContext; securityContext != nil && securityContext.Privileged != nil {
privilegedAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNamePrivileged,
SaveType: "string",
AttributeValue: strconv.FormatBool(*securityContext.Privileged),
}
attributes = append(attributes, privilegedAttributes)
}
*parameter.ComponentsCR = append(*parameter.ComponentsCR, model.ConvertResource{
ComponentsName: parameter.Name,
BasicManagement: parameter.Basic,
PortManagement: ps,
ENVManagement: envs,
ConfigManagement: configs,
TelescopicManagement: t,
HealthyCheckManagement: hcm,
ComponentK8sAttributesManagement: attributes,
})
}
func ObjectToJSONORYaml(changeType string, data interface{}) (string, error) {
if data == nil {
return "", nil
}
dataJSON, err := json.Marshal(data)
if err != nil {
return "", fmt.Errorf("json serialization failed err:%v", err)
}
if changeType == "json" {
return string(dataJSON), nil
}
dataYaml, err := yaml.JSONToYAML(dataJSON)
if err != nil {
return "", fmt.Errorf("yaml serialization failed err:%v", err)
}
return string(dataYaml), nil
}