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  }