config/bootstrap.go
Rama main · réplica de lectura del repositorio original.
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
}