upda/server/service_update.go
Varakh 5763d77045
All checks were successful
/ build (push) Successful in 3m37s
Added locking, improve prometheus handling and change to more reasonable configuration defaults for background tasks
- Disable cleaning up stale updates and events by default
- Change Prometheus exporter behavior
  - Return -1 for deleted updates in Prometheus which are evicted on next application restart
  - Ignore PROMETHEUS_METRICS_PATH (defaults to /metrics) in application metrics
- Improve template Grafana Dashboard
- Add locking for background task in non-distributed environments to avoid overlaps
2023-12-23 22:51:51 +01:00

143 lines
3.6 KiB
Go

package server
import (
"errors"
"git.myservermanager.com/varakh/upda/api"
"go.uber.org/zap"
"time"
)
type updateService struct {
repo updateRepository
eventService *eventService
prometheusService *prometheusService
}
func newUpdateService(r updateRepository, e *eventService, p *prometheusService) *updateService {
return &updateService{
repo: r,
eventService: e,
prometheusService: p,
}
}
func (s *updateService) get(id string) (*Update, error) {
if id == "" {
return nil, errorValidationNotBlank
}
return s.repo.find(id)
}
func (s *updateService) getAll() ([]*Update, error) {
return s.repo.findAll()
}
func (s *updateService) upsert(application string, provider string, host string, version string, metadata interface{}) (*Update, error) {
if application == "" || provider == "" || host == "" || version == "" {
return nil, errorValidationNotBlank
}
var e *Update
var err error
e, err = s.repo.findBy(application, provider, host)
if err != nil && !errors.Is(err, errorResourceNotFound) {
return nil, err
} else if err != nil && errors.Is(err, errorResourceNotFound) {
if e, err = s.repo.create(application, provider, host, version, metadata); err != nil {
return nil, err
}
s.eventService.createUpdateCreated(e)
zap.L().Sugar().Infof("Created update '%v'", e)
} else {
old := e
skip := e.State == api.UpdateStateIgnored.Value()
if skip {
zap.L().Sugar().Infof("Skipping ignored update '%v'", e.ID)
return nil, nil
}
if e, err = s.repo.update(e.ID.String(), version, metadata); err != nil {
return nil, err
}
s.eventService.createUpdateUpdated(old, e)
zap.L().Sugar().Infof("Updated update '%v'", e)
if api.UpdateStateApproved.Value() == e.State {
zap.L().Sugar().Infof("Setting update '%v' state to '%v'", e.ID, api.UpdateStatePending)
if e, err = s.repo.updateState(e.ID.String(), api.UpdateStatePending); err != nil {
return nil, err
}
}
}
return e, err
}
func (s *updateService) updateState(id string, state api.UpdateState) (*Update, error) {
if id == "" || state == "" {
return nil, errorValidationNotBlank
}
var e *Update
var err error
if e, err = s.get(id); err != nil {
return nil, err
}
oldUpdate := e
if e, err = s.repo.updateState(id, state); err != nil {
return nil, err
}
s.eventService.createUpdateUpdated(oldUpdate, e)
zap.L().Sugar().Infof("Modified update '%v'", id)
return e, nil
}
func (s *updateService) delete(id string) error {
if id == "" {
return errorValidationNotBlank
}
var e *Update
var err error
if e, err = s.get(id); err != nil {
return err
}
if _, err = s.repo.delete(id); err != nil {
return err
}
s.eventService.createUpdateDeleted(e)
if err = s.prometheusService.setGauge(metricUpdates, []string{e.Application, e.Provider, e.Host}, -1); err != nil {
zap.L().Sugar().Errorf("Could not refresh updates prometheus metric for deleted update '%v'. Reason: %v", e.ID, err)
}
zap.L().Sugar().Infof("Deleted update '%v'", id)
return nil
}
func (s *updateService) cleanStale(time time.Time, state ...api.UpdateState) (int64, error) {
if len(state) == 0 {
return 0, errorValidationNotEmpty
}
return s.repo.deleteByUpdatedAtBeforeAndStates(time, state...)
}
func (s *updateService) paginate(page int, pageSize int, orderBy string, order string, searchTerm string, searchIn string, state ...api.UpdateState) ([]*Update, error) {
return s.repo.paginate(page, pageSize, orderBy, order, searchTerm, searchIn, state...)
}
func (s *updateService) count(searchTerm string, searchIn string, state ...api.UpdateState) (int64, error) {
return s.repo.count(searchTerm, searchIn, state...)
}