Rainbond/api/handler/resource_import.go
2022-11-15 09:28:32 +08:00

426 lines
17 KiB
Go

package handler
import (
"context"
"encoding/json"
"fmt"
"github.com/goodrain/rainbond/api/model"
"github.com/goodrain/rainbond/api/util"
"github.com/goodrain/rainbond/db"
dbmodel "github.com/goodrain/rainbond/db/model"
rainbondutil "github.com/goodrain/rainbond/util"
"github.com/goodrain/rainbond/util/constants"
"github.com/sirupsen/logrus"
"github.com/twinj/uuid"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"strings"
"time"
)
//ResourceImport Import the converted k8s resources into recognition
func (c *clusterAction) ResourceImport(namespace string, as map[string]model.ApplicationResource, eid string) (*model.ReturnResourceImport, *util.APIHandleError) {
logrus.Infof("ResourceImport function begin")
var returnResourceImport model.ReturnResourceImport
tenant, err := c.createTenant(eid, namespace)
returnResourceImport.Tenant = tenant
if err != nil {
logrus.Errorf("%v", err)
return nil, &util.APIHandleError{Code: 400, Err: fmt.Errorf("create tenant error:%v", err)}
}
for appName, components := range as {
app, err := c.createApp(eid, appName, tenant.UUID)
if err != nil {
logrus.Errorf("create app:%v err:%v", appName, err)
return nil, &util.APIHandleError{Code: 400, Err: fmt.Errorf("create app:%v error:%v", appName, err)}
}
k8sResource, err := c.CreateK8sResource(components.KubernetesResources, app.AppID)
if err != nil {
logrus.Errorf("create K8sResources err:%v", err)
return nil, &util.APIHandleError{Code: 400, Err: fmt.Errorf("create K8sResources err:%v", err)}
}
var componentAttributes []model.ComponentAttributes
for _, componentResource := range components.ConvertResource {
component, err := c.CreateComponent(app, tenant.UUID, componentResource, namespace, false)
if err != nil {
logrus.Errorf("%v", err)
return nil, &util.APIHandleError{Code: 400, Err: fmt.Errorf("create app error:%v", err)}
}
c.createENV(componentResource.ENVManagement, component)
c.createConfig(componentResource.ConfigManagement, component)
c.createPort(componentResource.PortManagement, component)
componentResource.TelescopicManagement.RuleID = c.createTelescopic(componentResource.TelescopicManagement, component)
componentResource.HealthyCheckManagement.ProbeID = c.createHealthyCheck(componentResource.HealthyCheckManagement, component)
c.createK8sAttributes(componentResource.ComponentK8sAttributesManagement, tenant.UUID, component)
componentAttributes = append(componentAttributes, model.ComponentAttributes{
TS: component,
Image: componentResource.BasicManagement.Image,
Cmd: componentResource.BasicManagement.Cmd,
ENV: componentResource.ENVManagement,
Config: componentResource.ConfigManagement,
Port: componentResource.PortManagement,
Telescopic: componentResource.TelescopicManagement,
HealthyCheck: componentResource.HealthyCheckManagement,
ComponentK8sAttributes: componentResource.ComponentK8sAttributesManagement,
})
}
application := model.AppComponent{
App: app,
Component: componentAttributes,
K8sResources: k8sResource,
}
returnResourceImport.App = append(returnResourceImport.App, application)
}
if err != nil {
return nil, &util.APIHandleError{Code: 400, Err: fmt.Errorf("resource import error:%v", err)}
}
logrus.Infof("ResourceImport function end")
return &returnResourceImport, nil
}
func (c *clusterAction) createTenant(eid string, namespace string) (*dbmodel.Tenants, error) {
logrus.Infof("begin create tenant")
var dbts dbmodel.Tenants
id, name, errN := GetServiceManager().CreateTenandIDAndName(eid)
if errN != nil {
return nil, errN
}
dbts.EID = eid
dbts.Namespace = namespace
dbts.Name = name
dbts.UUID = id
dbts.LimitMemory = 0
tenant, _ := db.GetManager().TenantDao().GetTenantIDByName(dbts.Name)
if tenant != nil {
logrus.Warningf("tenant %v already exists", dbts.Name)
return tenant, nil
}
if err := db.GetManager().TenantDao().AddModel(&dbts); err != nil {
if !strings.HasSuffix(err.Error(), "is exist") {
return nil, err
}
}
ns, err := c.clientset.CoreV1().Namespaces().Get(context.Background(), namespace, metav1.GetOptions{})
if err != nil {
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to get namespace %v:%v", namespace, err)}
}
if ns.Labels == nil {
ns.Labels = make(map[string]string)
}
ns.Labels[constants.ResourceManagedByLabel] = constants.Rainbond
_, err = c.clientset.CoreV1().Namespaces().Update(context.Background(), ns, metav1.UpdateOptions{})
if err != nil {
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to add label to namespace %v:%v", namespace, err)}
}
logrus.Infof("end create tenant")
return &dbts, nil
}
func (c *clusterAction) createApp(eid string, app string, tenantID string) (*dbmodel.Application, error) {
appID := rainbondutil.NewUUID()
application, _ := db.GetManager().ApplicationDao().GetAppByName(tenantID, app)
if application != nil {
logrus.Infof("app %v already exists", app)
return application, nil
}
appReq := &dbmodel.Application{
EID: eid,
TenantID: tenantID,
AppID: appID,
AppName: app,
AppType: "rainbond",
GovernanceMode: dbmodel.GovernanceModeKubernetesNativeService,
K8sApp: app,
}
if err := db.GetManager().ApplicationDao().AddModel(appReq); err != nil {
return appReq, err
}
return appReq, nil
}
func (c *clusterAction) CreateK8sResource(k8sResources []dbmodel.K8sResource, AppID string) ([]dbmodel.K8sResource, error) {
var k8sResourceList []*dbmodel.K8sResource
for _, k8sResource := range k8sResources {
k8sResource.AppID = AppID
kr := k8sResource
k8sResourceList = append(k8sResourceList, &kr)
}
err := db.GetManager().K8sResourceDao().CreateK8sResource(k8sResourceList)
return k8sResources, err
}
func (c *clusterAction) CreateComponent(app *dbmodel.Application, tenantID string, component model.ConvertResource, namespace string, isYaml bool) (*dbmodel.TenantServices, error) {
var extendMethod string
switch component.BasicManagement.ResourceType {
case model.Deployment:
extendMethod = string(dbmodel.ServiceTypeStatelessMultiple)
case model.Job:
extendMethod = string(dbmodel.ServiceTypeJob)
case model.CronJob:
extendMethod = string(dbmodel.ServiceTypeCronJob)
case model.StateFulSet:
extendMethod = string(dbmodel.ServiceTypeStateMultiple)
}
serviceID := rainbondutil.NewUUID()
serviceAlias := "gr" + serviceID[len(serviceID)-6:]
replicas := 1
if component.BasicManagement.Replicas != nil {
replicas = int(*component.BasicManagement.Replicas)
}
JobStrategy, err := json.Marshal(component.BasicManagement.JobStrategy)
if err != nil {
logrus.Errorf("component %v BasicManagement.JobStrategy json error%v", component.ComponentsName, err)
}
ts := dbmodel.TenantServices{
TenantID: tenantID,
ServiceID: serviceID,
ServiceAlias: serviceAlias,
ServiceName: serviceAlias,
ServiceType: "application",
Comment: "docker run application",
ContainerCPU: int(component.BasicManagement.CPU),
ContainerMemory: int(component.BasicManagement.Memory),
ContainerGPU: 0,
UpgradeMethod: "Rolling",
ExtendMethod: extendMethod,
Replicas: replicas,
DeployVersion: time.Now().Format("20060102150405"),
Category: "app_publish",
CurStatus: "undeploy",
Status: 0,
Namespace: namespace,
UpdateTime: time.Now(),
Kind: "internal",
AppID: app.AppID,
K8sComponentName: component.ComponentsName,
JobStrategy: string(JobStrategy),
}
if err := db.GetManager().TenantServiceDao().AddModel(&ts); err != nil {
logrus.Errorf("add service error, %v", err)
return nil, err
}
if !isYaml {
changeLabel := func(label map[string]string) map[string]string {
label[constants.ResourceManagedByLabel] = constants.Rainbond
label["service_id"] = serviceID
label["version"] = ts.DeployVersion
label["creater_id"] = string(rainbondutil.NewTimeVersion())
label["migrator"] = "rainbond"
label["creator"] = "Rainbond"
return label
}
switch component.BasicManagement.ResourceType {
case model.Deployment:
dm, err := c.clientset.AppsV1().Deployments(namespace).Get(context.Background(), component.ComponentsName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("failed to get %v Deployments %v:%v", namespace, component.ComponentsName, err)
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to get Deployments %v:%v", namespace, err)}
}
if dm.Labels == nil {
dm.Labels = make(map[string]string)
}
if dm.Spec.Template.Labels == nil {
dm.Spec.Template.Labels = make(map[string]string)
}
dm.Labels = changeLabel(dm.Labels)
dm.Spec.Template.Labels = changeLabel(dm.Spec.Template.Labels)
_, err = c.clientset.AppsV1().Deployments(namespace).Update(context.Background(), dm, metav1.UpdateOptions{})
if err != nil {
logrus.Errorf("failed to update Deployments %v:%v", namespace, err)
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to update Deployments %v:%v", namespace, err)}
}
case model.Job:
job, err := c.clientset.BatchV1().Jobs(namespace).Get(context.Background(), component.ComponentsName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("failed to get %v Jobs %v:%v", namespace, component.ComponentsName, err)
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to get Jobs %v:%v", namespace, err)}
}
if job.Labels == nil {
job.Labels = make(map[string]string)
}
job.Labels = changeLabel(job.Labels)
_, err = c.clientset.BatchV1().Jobs(namespace).Update(context.Background(), job, metav1.UpdateOptions{})
if err != nil {
logrus.Errorf("failed to update StatefulSets %v:%v", namespace, err)
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to update StatefulSets %v:%v", namespace, err)}
}
case model.CronJob:
cr, err := c.clientset.BatchV1beta1().CronJobs(namespace).Get(context.Background(), component.ComponentsName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("failed to get %v CronJob %v:%v", namespace, component.ComponentsName, err)
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to get CronJob %v:%v", namespace, err)}
}
if cr.Labels == nil {
cr.Labels = make(map[string]string)
}
cr.Labels = changeLabel(cr.Labels)
if cr.Spec.JobTemplate.Labels == nil {
cr.Spec.JobTemplate.Labels = make(map[string]string)
}
cr.Spec.JobTemplate.Labels = changeLabel(cr.Spec.JobTemplate.Labels)
_, err = c.clientset.BatchV1beta1().CronJobs(namespace).Update(context.Background(), cr, metav1.UpdateOptions{})
if err != nil {
logrus.Errorf("failed to update CronJobs %v:%v", namespace, err)
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to update CronJobs %v:%v", namespace, err)}
}
case model.StateFulSet:
sfs, err := c.clientset.AppsV1().StatefulSets(namespace).Get(context.Background(), component.ComponentsName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("failed to get %v StatefulSets %v:%v", namespace, component.ComponentsName, err)
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to get StatefulSets %v:%v", namespace, err)}
}
if sfs.Labels == nil {
sfs.Labels = make(map[string]string)
}
sfs.Labels = changeLabel(sfs.Labels)
if sfs.Spec.Template.Labels == nil {
sfs.Spec.Template.Labels = make(map[string]string)
}
sfs.Spec.Template.Labels = changeLabel(sfs.Spec.Template.Labels)
_, err = c.clientset.AppsV1().StatefulSets(namespace).Update(context.Background(), sfs, metav1.UpdateOptions{})
if err != nil {
logrus.Errorf("failed to update StatefulSets %v:%v", namespace, err)
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to update StatefulSets %v:%v", namespace, err)}
}
}
}
return &ts, nil
}
func (c *clusterAction) createENV(envs []model.ENVManagement, service *dbmodel.TenantServices) {
var envVar []*dbmodel.TenantServiceEnvVar
for _, env := range envs {
var envD dbmodel.TenantServiceEnvVar
envD.AttrName = env.ENVKey
envD.AttrValue = env.ENVValue
envD.TenantID = service.TenantID
envD.ServiceID = service.ServiceID
envD.ContainerPort = 0
envD.IsChange = true
envD.Name = env.ENVExplain
envD.Scope = "inner"
envVar = append(envVar, &envD)
}
if err := db.GetManager().TenantServiceEnvVarDao().CreateOrUpdateEnvsInBatch(envVar); err != nil {
logrus.Errorf("%v Environment variable creation failed:%v", service.ServiceAlias, err)
}
}
func (c *clusterAction) createConfig(configs []model.ConfigManagement, service *dbmodel.TenantServices) {
var configVar []*dbmodel.TenantServiceVolume
for _, config := range configs {
tsv := &dbmodel.TenantServiceVolume{
ServiceID: service.ServiceID,
VolumeName: config.ConfigName,
VolumePath: config.ConfigPath,
VolumeType: "config-file",
Category: "",
VolumeProviderName: "",
IsReadOnly: false,
VolumeCapacity: 0,
AccessMode: "RWX",
SharePolicy: "exclusive",
BackupPolicy: "exclusive",
ReclaimPolicy: "exclusive",
AllowExpansion: false,
Mode: &config.Mode,
}
configVar = append(configVar, tsv)
}
err := db.GetManager().TenantServiceVolumeDao().CreateOrUpdateVolumesInBatch(configVar)
if err != nil {
logrus.Errorf("%v configuration file creation failed:%v", service.ServiceAlias, err)
}
}
func (c *clusterAction) createPort(ports []model.PortManagement, service *dbmodel.TenantServices) {
var portVar []*dbmodel.TenantServicesPort
for _, port := range ports {
portAlias := strings.Replace(service.ServiceAlias, "-", "_", -1)
var vpD dbmodel.TenantServicesPort
vpD.ServiceID = service.ServiceID
vpD.TenantID = service.TenantID
vpD.IsInnerService = &port.Inner
vpD.IsOuterService = &port.Outer
vpD.ContainerPort = int(port.Port)
vpD.MappingPort = int(port.Port)
vpD.Name = port.Name
vpD.Protocol = port.Protocol
vpD.PortAlias = fmt.Sprintf("%v%v", strings.ToUpper(portAlias), port.Port)
vpD.K8sServiceName = fmt.Sprintf("%v-%v", service.ServiceAlias, port.Port)
portVar = append(portVar, &vpD)
}
if err := db.GetManager().TenantServicesPortDao().CreateOrUpdatePortsInBatch(portVar); err != nil {
logrus.Errorf("%v port creation failed:%v", service.ServiceAlias, err)
}
}
func (c *clusterAction) createTelescopic(telescopic model.TelescopicManagement, service *dbmodel.TenantServices) string {
if !telescopic.Enable {
return ""
}
r := &dbmodel.TenantServiceAutoscalerRules{
RuleID: rainbondutil.NewUUID(),
ServiceID: service.ServiceID,
Enable: true,
XPAType: "hpa",
MinReplicas: int(telescopic.MinReplicas),
MaxReplicas: int(telescopic.MaxReplicas),
}
telescopic.RuleID = r.RuleID
if err := db.GetManager().TenantServceAutoscalerRulesDao().AddModel(r); err != nil {
logrus.Errorf("%v TenantServiceAutoscalerRules creation failed:%v", service.ServiceAlias, err)
return ""
}
for _, metric := range telescopic.CPUOrMemory {
m := &dbmodel.TenantServiceAutoscalerRuleMetrics{
RuleID: r.RuleID,
MetricsType: metric.MetricsType,
MetricsName: metric.MetricsName,
MetricTargetType: metric.MetricTargetType,
MetricTargetValue: metric.MetricTargetValue,
}
if err := db.GetManager().TenantServceAutoscalerRuleMetricsDao().AddModel(m); err != nil {
logrus.Errorf("%v TenantServceAutoscalerRuleMetricsDao creation failed:%v", service.ServiceAlias, err)
}
}
return r.RuleID
}
func (c *clusterAction) createHealthyCheck(telescopic model.HealthyCheckManagement, service *dbmodel.TenantServices) string {
if telescopic.Status == 0 {
return ""
}
var tspD dbmodel.TenantServiceProbe
tspD.ServiceID = service.ServiceID
tspD.Cmd = telescopic.Command
tspD.FailureThreshold = telescopic.FailureThreshold
tspD.HTTPHeader = telescopic.HTTPHeader
tspD.InitialDelaySecond = telescopic.InitialDelaySecond
tspD.IsUsed = &telescopic.Status
tspD.Mode = telescopic.Mode
tspD.Path = telescopic.Path
tspD.PeriodSecond = telescopic.PeriodSecond
tspD.Port = telescopic.Port
tspD.ProbeID = strings.Replace(uuid.NewV4().String(), "-", "", -1)
tspD.Scheme = telescopic.DetectionMethod
tspD.SuccessThreshold = telescopic.SuccessThreshold
tspD.TimeoutSecond = telescopic.TimeoutSecond
tspD.FailureAction = ""
if err := GetServiceManager().ServiceProbe(&tspD, "add"); err != nil {
logrus.Errorf("%v createHealthyCheck creation failed:%v", service.ServiceAlias, err)
}
return tspD.ProbeID
}
func (c *clusterAction) createK8sAttributes(specials []*dbmodel.ComponentK8sAttributes, tenantID string, component *dbmodel.TenantServices) {
for _, specials := range specials {
specials.TenantID = tenantID
specials.ComponentID = component.ServiceID
}
err := db.GetManager().ComponentK8sAttributeDao().CreateOrUpdateAttributesInBatch(specials)
if err != nil {
logrus.Errorf("%v createSpecial creation failed:%v", component.ServiceAlias, err)
}
}