Ctrl K
ComunidadGuías…

config/bootstrap.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package config

import (
	"fmt"

	"github.com/coroot/coroot/cloud"
	"github.com/coroot/coroot/db"
	"github.com/coroot/coroot/model"
	"golang.org/x/exp/maps"
	"k8s.io/klog"
)

func (cfg *Config) Bootstrap(database *db.DB) error {
	if cfg.CorootCloud != nil {
		cloudAPI := cloud.API(database, "", "", "")
		err := cloudAPI.SaveSettings(*cfg.CorootCloud)
		if err != nil {
			return err
		}
	}

	if len(cfg.Projects) == 0 {
		p, err := getOrCreateDefaultProject(database)
		if err != nil {
			return err
		}
		if p != nil {
			prometheus := cfg.GetBootstrapPrometheus()
			if p.Prometheus.Url == "" && prometheus != nil && prometheus.Url != "" && prometheus.RefreshInterval > 0 {
				p.Prometheus = *prometheus
				err = database.SaveProjectIntegration(p, db.IntegrationTypePrometheus)
				if err != nil {
					return err
				}
			}
			clickhouse := cfg.GetBootstrapClickhouse()
			if p.Settings.Integrations.Clickhouse == nil && clickhouse != nil && clickhouse.Addr != "" {
				p.Settings.Integrations.Clickhouse = clickhouse
				err = database.SaveProjectIntegration(p, db.IntegrationTypeClickhouse)
				if err != nil {
					return err
				}
			}
		}
	}

	ps, err := database.GetProjects()
	if err != nil {
		return err
	}
	byName := map[string]*db.Project{}
	for _, p := range ps {
		byName[p.Name] = p
		p.Settings.Readonly = false
		p.Settings.Integrations.NotificationIntegrations.Readonly = false
	}
	type projectInspectionOverrides struct {
		project   *db.Project
		overrides *InspectionOverrides
	}
	var inspectionOverrides []projectInspectionOverrides
	for _, p := range cfg.Projects {
		pp := byName[p.Name]
		if pp == nil {
			pp = &db.Project{Name: p.Name}
			klog.Infoln("creating project:", pp.Name)
			err = database.SaveProject(pp)
			if err != nil {
				return err
			}
			byName[pp.Name] = pp
		}
		pp.Settings.Readonly = true
		pp.Settings.MemberProjects = p.MemberProjects
		pp.Settings.ApiKeys = p.ApiKeys
		if p.NotificationIntegrations != nil {
			pp.Settings.Integrations.NotificationIntegrations = *p.NotificationIntegrations
		}

		if p.RemoteCoroot != nil {
			pp.Prometheus = *p.RemoteCoroot.PrometheusConfig()
			if err = database.SaveProjectIntegration(pp, db.IntegrationTypePrometheus); err != nil {
				return err
			}
			pp.Settings.Integrations.Clickhouse = p.RemoteCoroot.ClickHouseConfig()
			if err = database.SaveProjectSettings(pp); err != nil {
				return err
			}
		}

		pp.Settings.Integrations.NotificationIntegrations.Readonly = p.NotificationIntegrations != nil
		if len(p.ApplicationCategories) > 0 {
			pp.Settings.ApplicationCategorySettings = map[model.ApplicationCategory]*db.ApplicationCategorySettings{}
			for _, c := range p.ApplicationCategories {
				pp.Settings.ApplicationCategorySettings[c.Name] = &c.ApplicationCategorySettings
			}
		}
		if len(p.CustomApplications) > 0 {
			pp.Settings.CustomApplications = map[string]model.CustomApplication{}
			for _, c := range p.CustomApplications {
				pp.Settings.CustomApplications[c.Name] = c.CustomApplication
			}
		}
		if p.InspectionOverrides != nil {
			inspectionOverrides = append(inspectionOverrides, projectInspectionOverrides{project: pp, overrides: p.InspectionOverrides})
		}
	}
	for _, po := range inspectionOverrides {
		for _, override := range po.overrides.SLOAvailability {
			c := []model.CheckConfigSLOAvailability{{
				Custom:              false,
				ObjectivePercentage: override.ObjectivePercent,
			}}
			if err = saveInspectionOverride(database, byName, po.project, override.ApplicationId, model.Checks.SLOAvailability.Id, c); err != nil {
				return err
			}
		}
		for _, override := range po.overrides.SLOLatency {
			c := []model.CheckConfigSLOLatency{{
				Custom:              false,
				ObjectivePercentage: override.ObjectivePercent,
				ObjectiveBucket:     model.RoundUpToDefaultBucket(float32(override.ObjectiveThreshold.Seconds())),
			}}
			if err = saveInspectionOverride(database, byName, po.project, override.ApplicationId, model.Checks.SLOLatency.Id, c); err != nil {
				return err
			}
		}
		for _, override := range po.overrides.SLOLLMLatency {
			c := []model.CheckConfigSLOLatency{{
				Custom:              false,
				ObjectivePercentage: override.ObjectivePercent,
				ObjectiveBucket:     model.RoundUpToBucket(float32(override.ObjectiveThreshold.Seconds()), model.LLMHistogramBuckets),
			}}
			if err = saveInspectionOverride(database, byName, po.project, override.ApplicationId, model.Checks.SLOLLMLatency.Id, c); err != nil {
				return err
			}
		}
	}
	for _, p := range byName {
		if p.Settings.ApiKeys == nil {
			p.Settings.ApiKeys = append(p.Settings.ApiKeys, db.ApiKey{Key: string(p.Id), Description: "default"})
		}
		err = database.SaveProjectSettings(p)
		if err != nil {
			return err
		}
	}

	if cfg.DisableBuiltinAlerts {
		for _, p := range byName {
			if err := database.DisableBuiltinAlertingRules(p.Id); err != nil {
				klog.Errorln("failed to disable builtin alerting rules:", err)
			}
		}
	}

	for _, p := range cfg.Projects {
		pp := byName[p.Name]
		if pp == nil {
			continue
		}
		if len(p.AlertingRules) > 0 {
			if err := syncConfigAlertingRules(database, pp.Id, p.AlertingRules); err != nil {
				klog.Errorln("failed to sync config alerting rules:", err)
			}
		} else {
			if err := database.ClearAlertingRulesReadonly(pp.Id); err != nil {
				klog.Errorln("failed to clear alerting rules readonly:", err)
			}
		}
	}

	return syncServiceAccounts(database, cfg.Auth.ServiceAccounts)
}

func syncServiceAccounts(database *db.DB, accounts []ServiceAccount) error {
	if len(accounts) == 0 {
		return nil
	}
	users, err := database.GetUsers()
	if err != nil {
		return err
	}
	byLogin := map[string]*db.User{}
	for _, u := range users {
		byLogin[u.Email] = u
	}
	for _, sa := range accounts {
		u := byLogin[sa.Name]
		switch {
		case u == nil:
			klog.Infoln("creating service account:", sa.Name)
			id, err := database.AddServiceAccount(sa.Name, sa.Name, sa.Role)
			if err != nil {
				return err
			}
			u = &db.User{Id: id}
		case !u.IsServiceAccount():
			return fmt.Errorf("service account '%s' conflicts with an existing user", sa.Name)
		default:
			if err = database.UpdateUser(u.Id, u.Email, "", u.Name, sa.Role); err != nil {
				return err
			}
		}
		if err = database.SetUserApiKeys(u.Id, sa.ApiKeys); err != nil {
			return err
		}
	}
	return nil
}

func saveInspectionOverride(database *db.DB, projectsByName map[string]*db.Project, project *db.Project, appId model.ApplicationId, checkId model.CheckId, cfg any) error {
	if !project.Multicluster() {
		return database.SaveCheckConfig(project.Id, appId, checkId, cfg)
	}
	if appId.ClusterId != "" && appId.ClusterId != model.ClusterIdExternal {
		for _, mp := range project.Settings.MemberProjects {
			p := projectsByName[mp]
			if p == nil {
				continue
			}
			if p.ClusterId() == appId.ClusterId {
				return database.SaveCheckConfig(p.Id, appId, checkId, cfg)
			}
		}
		klog.Warningf("skipping inspection override for %s: cluster %s is not a member of project %s", appId, appId.ClusterId, project.Name)
		return nil
	}
	for _, mp := range project.Settings.MemberProjects {
		p := projectsByName[mp]
		if p == nil {
			klog.Warningf("skipping inspection override for %s: member project %s of project %s not found", appId, mp, project.Name)
			continue
		}
		if err := database.SaveCheckConfig(p.Id, appId, checkId, cfg); err != nil {
			return err
		}
	}
	return nil
}

func syncConfigAlertingRules(database *db.DB, projectId db.ProjectId, configRules []AlertingRule) error {
	if err := database.ClearAlertingRulesReadonly(projectId); err != nil {
		return err
	}
	existing, err := database.GetAlertingRules(projectId)
	if err != nil {
		return err
	}
	byId := map[model.AlertingRuleId]*model.AlertingRule{}
	for _, r := range existing {
		byId[r.Id] = r
	}
	for _, cr := range configRules {
		ruleId := model.AlertingRuleId(cr.Id)
		if existingRule, ok := byId[ruleId]; ok {
			merged := applyConfigOverrides(existingRule, cr)
			merged.Readonly = true
			if err := database.UpdateAlertingRule(projectId, merged); err != nil {
				return err
			}
		} else {
			base := &model.AlertingRule{
				Id:       ruleId,
				Readonly: true,
				Enabled:  true,
			}
			newRule := applyConfigOverrides(base, cr)
			newRule.ProjectId = string(projectId)
			if err := database.CreateAlertingRule(projectId, newRule); err != nil {
				return err
			}
		}
	}
	return nil
}

func getOrCreateDefaultProject(database *db.DB) (*db.Project, error) {
	projects, err := database.GetProjects()
	if err != nil {
		return nil, err
	}
	switch len(projects) {
	case 0:
		p := &db.Project{Name: "default"}
		klog.Infoln("creating project:", p.Name)
		if err = database.SaveProject(p); err != nil {
			return nil, err
		}
		return p, nil
	case 1:
		return maps.Values(projects)[0], nil
	}
	return nil, nil
}