github.com/caos/orbos@v1.5.14-0.20221103111702-e6cd0cea7ad4/internal/operator/boom/bundle/bundle.go (about) 1 package bundle 2 3 import ( 4 "errors" 5 "fmt" 6 "sync" 7 8 "github.com/caos/orbos/internal/operator/boom/api/latest" 9 10 "github.com/caos/orbos/internal/operator/boom/metrics" 11 12 "github.com/caos/orbos/internal/operator/boom/application" 13 "github.com/caos/orbos/internal/operator/boom/bundle/bundles" 14 "github.com/caos/orbos/internal/operator/boom/bundle/config" 15 "github.com/caos/orbos/internal/operator/boom/current" 16 "github.com/caos/orbos/internal/operator/boom/name" 17 "github.com/caos/orbos/internal/operator/boom/templator" 18 "github.com/caos/orbos/internal/operator/boom/templator/helm" 19 helperTemp "github.com/caos/orbos/internal/operator/boom/templator/helper" 20 "github.com/caos/orbos/internal/operator/boom/templator/yaml" 21 "github.com/caos/orbos/internal/utils/clientgo" 22 "github.com/caos/orbos/mntr" 23 ) 24 25 var ( 26 Testmode bool = false 27 ) 28 29 type Bundle struct { 30 baseDirectoryPath string 31 crdName string 32 predefinedBundle name.Bundle 33 orb string 34 Applications map[name.Application]application.Application 35 HelmTemplator templator.Templator 36 YamlTemplator templator.Templator 37 monitor mntr.Monitor 38 } 39 40 func New(conf *config.Config) *Bundle { 41 apps := make(map[name.Application]application.Application, 0) 42 helmTemplator := helperTemp.NewTemplator(conf.Monitor, conf.CrdName, conf.BaseDirectoryPath, helm.GetName()) 43 yamlTemplator := helperTemp.NewTemplator(conf.Monitor, conf.CrdName, conf.BaseDirectoryPath, yaml.GetName()) 44 45 return &Bundle{ 46 crdName: conf.CrdName, 47 orb: conf.Orb, 48 baseDirectoryPath: conf.BaseDirectoryPath, 49 monitor: conf.Monitor, 50 HelmTemplator: helmTemplator, 51 YamlTemplator: yamlTemplator, 52 Applications: apps, 53 predefinedBundle: "", 54 } 55 } 56 57 func (b *Bundle) GetPredefinedBundle() string { 58 return b.predefinedBundle.String() 59 } 60 61 func (b *Bundle) CleanUp() error { 62 63 err := b.HelmTemplator.CleanUp() 64 if err != nil { 65 return err 66 } 67 68 return b.YamlTemplator.CleanUp() 69 } 70 71 func (b *Bundle) GetApplications() map[name.Application]application.Application { 72 return b.Applications 73 } 74 75 func (b *Bundle) AddApplicationsByBundleName(name name.Bundle) error { 76 77 appNames := bundles.Get(name) 78 if appNames == nil { 79 return fmt.Errorf("No bundle known with name %s", name) 80 } 81 b.predefinedBundle = name 82 83 for _, appName := range appNames { 84 if err := b.AddApplicationByName(appName); err != nil { 85 return err 86 } 87 } 88 return nil 89 } 90 91 func (b *Bundle) AddApplicationByName(appName name.Application) error { 92 93 app := application.New(b.monitor, appName, b.orb) 94 return b.AddApplication(app) 95 } 96 97 func (b *Bundle) AddApplication(app application.Application) error { 98 99 if _, found := b.Applications[app.GetName()]; found { 100 return errors.New("Application already in bundle") 101 } 102 103 b.Applications[app.GetName()] = app 104 return nil 105 } 106 107 func (b *Bundle) Reconcile(currentResourceList []*clientgo.Resource, spec *latest.ToolsetSpec) error { 108 109 applicationCount := 0 110 // go through list of application until every application is reconciled 111 // and this orderNumber by orderNumber (default is 1) 112 for orderNumber := 0; applicationCount < len(b.Applications); orderNumber++ { 113 var wg sync.WaitGroup 114 errList := make(map[name.Application]chan error, len(b.Applications)) 115 for appName := range b.Applications { 116 //if application has the same orderNumber as currently iterating the reconcile the application 117 if application.GetOrderNumber(appName) == orderNumber { 118 wg.Add(1) 119 errChan := make(chan error) 120 go b.ReconcileApplication(currentResourceList, appName, spec, &wg, errChan) 121 applicationCount++ 122 errList[appName] = errChan 123 } 124 } 125 for appName, errChan := range errList { 126 if err := <-errChan; err != nil { 127 return fmt.Errorf("Error while reconciling application %s: %w", appName.String(), err) 128 } 129 } 130 wg.Wait() 131 } 132 133 return nil 134 } 135 136 func (b *Bundle) ReconcileApplication(currentResourceList []*clientgo.Resource, appName name.Application, spec *latest.ToolsetSpec, wg *sync.WaitGroup, errChan chan error) { 137 defer wg.Done() 138 139 logFields := map[string]interface{}{ 140 "application": appName, 141 "action": "reconciling", 142 } 143 monitor := b.monitor.WithFields(logFields) 144 145 app, found := b.Applications[appName] 146 if !found { 147 err := errors.New("Application not found") 148 monitor.Error(err) 149 errChan <- err 150 return 151 } 152 monitor.Info("Start") 153 154 deploy := app.Deploy(spec) 155 currentApplicationResourceList := current.FilterForApplication(appName, currentResourceList) 156 157 var resultFunc func(string, string) error 158 if Testmode { 159 resultFunc = func(resultFilePath, namespace string) error { 160 return nil 161 } 162 } else { 163 if deploy { 164 resultFunc = applyWithCurrentState(monitor, currentApplicationResourceList, app, spec.ForceApply) 165 } else { 166 resultFunc = deleteWithCurrentState(monitor, currentApplicationResourceList, app) 167 } 168 } 169 170 _, usedHelm := app.(application.HelmApplication) 171 if usedHelm { 172 templatorName := helm.GetName() 173 err := b.HelmTemplator.Template(app, spec, resultFunc) 174 if err != nil { 175 metrics.FailureReconcilingApplication(appName.String(), templatorName.String(), deploy) 176 errChan <- err 177 return 178 } 179 metrics.SuccessfulReconcilingApplication(appName.String(), templatorName.String(), deploy) 180 } 181 _, usedYaml := app.(application.YAMLApplication) 182 if usedYaml { 183 templatorName := yaml.GetName() 184 err := b.YamlTemplator.Template(app, spec, resultFunc) 185 if err != nil { 186 metrics.FailureReconcilingApplication(appName.String(), templatorName.String(), deploy) 187 errChan <- err 188 return 189 } 190 metrics.SuccessfulReconcilingApplication(appName.String(), templatorName.String(), deploy) 191 } 192 193 monitor.Info("Done") 194 errChan <- nil 195 }