Rainbond/api/handler/get_namespace_resource_name.go
张启航 61dd996806
perf: optimized k8s resource processing mode (#1255)
* perf(api): optimized k8s processing mode

* perf(api): change cornjob to cronjob
2022-07-31 22:36:28 +08:00

526 lines
18 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handler
import (
"github.com/goodrain/rainbond/api/model"
"github.com/goodrain/rainbond/api/util"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
//GetNamespaceSource Get all resources in the current namespace
func (c *clusterAction) GetNamespaceSource(ctx context.Context, content string, namespace string) (map[string]model.LabelResource, *util.APIHandleError) {
logrus.Infof("GetNamespaceSource function begin")
//存储workloads们的ConfigMap
cmsMap := make(map[string][]string)
//存储workloads们的secrets
secretsMap := make(map[string][]string)
deployments, cmMap, secretMap := c.getResourceName(context.Background(), namespace, content, model.Deployment)
if len(cmsMap) != 0 {
cmsMap = MergeMap(cmMap, cmsMap)
}
if len(secretMap) != 0 {
secretsMap = MergeMap(secretMap, secretsMap)
}
jobs, cmMap, secretMap := c.getResourceName(ctx, namespace, content, model.Job)
if len(cmsMap) != 0 {
cmsMap = MergeMap(cmMap, cmsMap)
}
if len(secretMap) != 0 {
secretsMap = MergeMap(secretMap, secretsMap)
}
cronJobs, cmMap, secretMap := c.getResourceName(ctx, namespace, content, model.CronJob)
if len(cmsMap) != 0 {
cmsMap = MergeMap(cmMap, cmsMap)
}
if len(secretMap) != 0 {
secretsMap = MergeMap(secretMap, secretsMap)
}
stateFulSets, cmMap, secretMap := c.getResourceName(ctx, namespace, content, model.StateFulSet)
if len(cmsMap) != 0 {
cmsMap = MergeMap(cmMap, cmsMap)
}
if len(secretMap) != 0 {
secretsMap = MergeMap(secretMap, secretsMap)
}
processWorkloads := model.LabelWorkloadsResourceProcess{
Deployments: deployments,
Jobs: jobs,
CronJobs: cronJobs,
StateFulSets: stateFulSets,
}
services, _, _ := c.getResourceName(ctx, namespace, content, model.Service)
pvc, _, _ := c.getResourceName(ctx, namespace, content, model.PVC)
ingresses, _, _ := c.getResourceName(ctx, namespace, content, model.Ingress)
networkPolicies, _, _ := c.getResourceName(ctx, namespace, content, model.NetworkPolicy)
cms, _, _ := c.getResourceName(ctx, namespace, content, model.ConfigMap)
secrets, _, _ := c.getResourceName(ctx, namespace, content, model.Secret)
serviceAccounts, _, _ := c.getResourceName(ctx, namespace, content, model.ServiceAccount)
roleBindings, _, _ := c.getResourceName(ctx, namespace, content, model.RoleBinding)
horizontalPodAutoscalers, _, _ := c.getResourceName(ctx, namespace, content, model.HorizontalPodAutoscaler)
roles, _, _ := c.getResourceName(ctx, namespace, content, model.Role)
processOthers := model.LabelOthersResourceProcess{
Services: services,
PVC: pvc,
Ingresses: ingresses,
NetworkPolicies: networkPolicies,
ConfigMaps: MergeMap(cmsMap, cms),
Secrets: MergeMap(secretsMap, secrets),
ServiceAccounts: serviceAccounts,
RoleBindings: roleBindings,
HorizontalPodAutoscalers: horizontalPodAutoscalers,
Roles: roles,
}
labelResource := resourceProcessing(processWorkloads, processOthers)
logrus.Infof("GetNamespaceSource function end")
return labelResource, nil
}
//resourceProcessing 将处理好的资源类型数据格式再加工成可作为返回值的数据。
func resourceProcessing(processWorkloads model.LabelWorkloadsResourceProcess, processOthers model.LabelOthersResourceProcess) map[string]model.LabelResource {
labelResource := make(map[string]model.LabelResource)
for label, deployments := range processWorkloads.Deployments {
if val, ok := labelResource[label]; ok {
val.Workloads.Deployments = deployments
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Workloads: model.WorkLoadsResource{
Deployments: deployments,
},
}
}
for label, jobs := range processWorkloads.Jobs {
if val, ok := labelResource[label]; ok {
val.Workloads.Jobs = jobs
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Workloads: model.WorkLoadsResource{
Jobs: jobs,
},
}
}
for label, cronJobs := range processWorkloads.CronJobs {
if val, ok := labelResource[label]; ok {
val.Workloads.CronJobs = cronJobs
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Workloads: model.WorkLoadsResource{
CronJobs: cronJobs,
},
}
}
for label, stateFulSets := range processWorkloads.StateFulSets {
if val, ok := labelResource[label]; ok {
val.Workloads.StateFulSets = stateFulSets
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Workloads: model.WorkLoadsResource{
StateFulSets: stateFulSets,
},
}
}
for label, service := range processOthers.Services {
if val, ok := labelResource[label]; ok {
val.Others.Services = service
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
Services: service,
},
}
}
for label, pvc := range processOthers.PVC {
if val, ok := labelResource[label]; ok {
val.Others.PVC = pvc
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
PVC: pvc,
},
}
}
for label, ingresses := range processOthers.Ingresses {
if val, ok := labelResource[label]; ok {
val.Others.Ingresses = ingresses
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
Ingresses: ingresses,
},
}
}
for label, networkPolicies := range processOthers.NetworkPolicies {
if val, ok := labelResource[label]; ok {
val.Others.NetworkPolicies = networkPolicies
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
NetworkPolicies: networkPolicies,
},
}
}
for label, configMaps := range processOthers.ConfigMaps {
if val, ok := labelResource[label]; ok {
val.Others.ConfigMaps = configMaps
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
ConfigMaps: configMaps,
},
}
}
for label, secrets := range processOthers.Secrets {
if val, ok := labelResource[label]; ok {
val.Others.Secrets = secrets
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
Secrets: secrets,
},
}
}
for label, serviceAccounts := range processOthers.ServiceAccounts {
if val, ok := labelResource[label]; ok {
val.Others.ServiceAccounts = serviceAccounts
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
ServiceAccounts: serviceAccounts,
},
}
}
for label, roleBindings := range processOthers.RoleBindings {
if val, ok := labelResource[label]; ok {
val.Others.RoleBindings = roleBindings
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
RoleBindings: roleBindings,
},
}
}
for label, horizontalPodAutoscalers := range processOthers.HorizontalPodAutoscalers {
if val, ok := labelResource[label]; ok {
val.Others.HorizontalPodAutoscalers = horizontalPodAutoscalers
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
HorizontalPodAutoscalers: horizontalPodAutoscalers,
},
}
}
for label, roles := range processOthers.Roles {
if val, ok := labelResource[label]; ok {
val.Others.Roles = roles
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
Roles: roles,
},
}
}
return labelResource
}
//Resource -
type Resource struct {
ObjectMeta metav1.ObjectMeta
Template corev1.PodTemplateSpec
}
//getResourceName 将指定资源类型按照【label名】[]{资源名...}处理后返回
func (c *clusterAction) getResourceName(ctx context.Context, namespace string, content string, resourcesType string) (map[string][]string, map[string][]string, map[string][]string) {
resourceName := make(map[string][]string)
var tempResources []*Resource
isWorkloads := false
cmMap := make(map[string][]string)
secretMap := make(map[string][]string)
switch resourcesType {
case model.Deployment:
resources, err := c.clientset.AppsV1().Deployments(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get Deployment list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta, Template: dm.Spec.Template})
}
isWorkloads = true
case model.Job:
resources, err := c.clientset.BatchV1().Jobs(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get Job list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta, Template: dm.Spec.Template})
}
isWorkloads = true
case model.CronJob:
resources, err := c.clientset.BatchV1beta1().CronJobs(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get CronJob list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta, Template: dm.Spec.JobTemplate.Spec.Template})
}
isWorkloads = true
case model.StateFulSet:
resources, err := c.clientset.AppsV1().StatefulSets(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get StateFulSets list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta, Template: dm.Spec.Template})
}
isWorkloads = true
case model.Service:
resources, err := c.clientset.CoreV1().Services(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get Services list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
case model.PVC:
resources, err := c.clientset.CoreV1().PersistentVolumeClaims(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get PersistentVolumeClaims list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
case model.Ingress:
resources, err := c.clientset.NetworkingV1().Ingresses(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get Ingresses list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
case model.NetworkPolicy:
resources, err := c.clientset.NetworkingV1().NetworkPolicies(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get NetworkPolicies list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
case model.ConfigMap:
resources, err := c.clientset.CoreV1().ConfigMaps(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get ConfigMaps list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
case model.Secret:
resources, err := c.clientset.CoreV1().Secrets(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get Secrets list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
case model.ServiceAccount:
resources, err := c.clientset.CoreV1().ServiceAccounts(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get ServiceAccounts list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
case model.RoleBinding:
resources, err := c.clientset.RbacV1().RoleBindings(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get RoleBindings list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
case model.HorizontalPodAutoscaler:
resources, err := c.clientset.AutoscalingV1().HorizontalPodAutoscalers(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get HorizontalPodAutoscalers list:%v", err)
return nil, cmMap, secretMap
}
for _, hpa := range resources.Items {
rbdResource := false
labels := make(map[string]string)
switch hpa.Spec.ScaleTargetRef.Kind {
case model.Deployment:
deploy, err := c.clientset.AppsV1().Deployments(namespace).Get(context.Background(), hpa.Spec.ScaleTargetRef.Name, metav1.GetOptions{})
if err != nil {
logrus.Errorf("The bound deployment does not exist:%v", err)
}
if hpa.ObjectMeta.Labels["creator"] == "Rainbond" {
rbdResource = true
}
labels = deploy.ObjectMeta.Labels
case model.StateFulSet:
ss, err := c.clientset.AppsV1().StatefulSets(namespace).Get(context.Background(), hpa.Spec.ScaleTargetRef.Name, metav1.GetOptions{})
if err != nil {
logrus.Errorf("The bound deployment does not exist:%v", err)
}
if hpa.ObjectMeta.Labels["creator"] == "Rainbond" {
rbdResource = true
}
labels = ss.ObjectMeta.Labels
}
var app string
if content == "unmanaged" && rbdResource {
continue
}
app = labels["app"]
if labels["app.kubernetes.io/name"] != "" {
app = labels["app.kubernetes.io/name"]
}
if app == "" {
app = "unclassified"
}
if _, ok := resourceName[app]; ok {
resourceName[app] = append(resourceName[app], hpa.Name)
} else {
resourceName[app] = []string{hpa.Name}
}
}
return resourceName, nil, nil
case model.Role:
resources, err := c.clientset.RbacV1().Roles(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get Roles list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
}
//这一块是统一处理资源按label划分出来
for _, rs := range tempResources {
if content == "unmanaged" && rs.ObjectMeta.Labels["creator"] == "Rainbond" {
continue
}
app := rs.ObjectMeta.Labels["app"]
if rs.ObjectMeta.Labels["app.kubernetes.io/name"] != "" {
app = rs.ObjectMeta.Labels["app.kubernetes.io/name"]
}
if app == "" {
app = "unclassified"
}
//如果是Workloads类型的资源需要检查其内部configmap、secret、PVC防止没有这三种资源没有label但是用到了
if isWorkloads {
cmList, secretList := c.replenishLabel(ctx, rs, namespace, app)
if _, ok := cmMap[app]; ok {
cmMap[app] = append(cmMap[app], cmList...)
} else {
cmMap[app] = cmList
}
if _, ok := secretMap[app]; ok {
secretMap[app] = append(secretMap[app], secretList...)
} else {
secretMap[app] = secretList
}
}
if _, ok := resourceName[app]; ok {
resourceName[app] = append(resourceName[app], rs.ObjectMeta.Name)
} else {
resourceName[app] = []string{rs.ObjectMeta.Name}
}
}
return resourceName, cmMap, secretMap
}
//replenishLabel 获取workloads资源上携带的ConfigMap和secret以及把pvc加上标签。
func (c *clusterAction) replenishLabel(ctx context.Context, resource *Resource, namespace string, app string) ([]string, []string) {
var cmList []string
var secretList []string
resourceVolume := resource.Template.Spec.Volumes
for _, volume := range resourceVolume {
if pvc := volume.PersistentVolumeClaim; pvc != nil {
PersistentVolumeClaims, err := c.clientset.CoreV1().PersistentVolumeClaims(namespace).Get(context.Background(), pvc.ClaimName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("Failed to get PersistentVolumeClaims %s/%s:%v", namespace, pvc.ClaimName, err)
}
if PersistentVolumeClaims.Labels == nil {
PersistentVolumeClaims.Labels = make(map[string]string)
}
if _, ok := PersistentVolumeClaims.Labels["app"]; !ok {
if _, ok := PersistentVolumeClaims.Labels["app.kubernetes.io/name"]; !ok {
PersistentVolumeClaims.Labels["app"] = app
}
}
_, err = c.clientset.CoreV1().PersistentVolumeClaims(namespace).Update(context.Background(), PersistentVolumeClaims, metav1.UpdateOptions{})
if err != nil {
logrus.Errorf("PersistentVolumeClaims label update error:%v", err)
}
continue
}
if cm := volume.ConfigMap; cm != nil {
cm, err := c.clientset.CoreV1().ConfigMaps(namespace).Get(context.Background(), cm.Name, metav1.GetOptions{})
if err != nil {
logrus.Errorf("Failed to get ConfigMap:%v", err)
}
if _, ok := cm.Labels["app"]; !ok {
if _, ok := cm.Labels["app.kubernetes.io/name"]; !ok {
cmList = append(cmList, cm.Name)
}
}
}
if secret := volume.Secret; secret != nil {
secret, err := c.clientset.CoreV1().Secrets(namespace).Get(context.Background(), secret.SecretName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("Failed to get Scret:%v", err)
}
if _, ok := secret.Labels["app"]; !ok {
if _, ok := secret.Labels["app.kubernetes.io/name"]; !ok {
cmList = append(cmList, secret.Name)
}
}
}
}
return cmList, secretList
}