Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

APPS-1431 Synchronize access to the service configuration #295

Merged
merged 18 commits into from
Jan 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions cmd/backup/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func startService(configFile string, remote bool) error {
return fmt.Errorf("failed to load configuration: %w", err)
}

appLogger := setDefaultLoggers(ctx, config)
appLogger := setDefaultLoggers(ctx, config.ServiceConfig.GetLoggerOrDefault())
slog.Info("Aerospike Backup Service", "commit", commit, "buildTime", buildTime)

// schedule all configured backups
Expand All @@ -83,13 +83,12 @@ func startService(configFile string, remote bool) error {

configApplier := service.NewDefaultConfigApplier(
scheduler,
config,
backends,
clientManager,
backupHandlers,
)

err = configApplier.ApplyNewConfig(ctx)
err = configApplier.ApplyNewRoutines(ctx, config.Routines())
if err != nil {
return fmt.Errorf("failed to apply new config: %w", err)
}
Expand All @@ -98,7 +97,7 @@ func startService(configFile string, remote bool) error {
service.NewMetricsCollector(backupHandlers, restoreJobs).Start(ctx, 1*time.Second)

restoreMgr := service.NewRestoreManager(
backends, config, service.NewRestore(), clientManager, restoreJobs, nsValidator)
backends, service.NewRestore(), clientManager, restoreJobs, nsValidator)

httpService := handlers.NewService(
config,
Expand All @@ -121,9 +120,9 @@ func startService(configFile string, remote bool) error {
return err
}

func setDefaultLoggers(ctx context.Context, config *model.Config) *slog.Logger {
func setDefaultLoggers(ctx context.Context, loggerConfig *model.LoggerConfig) *slog.Logger {
appLogger := slog.New(
util.LogHandler(config.ServiceConfig.GetLoggerOrDefault()),
util.LogHandler(loggerConfig),
)
slog.SetDefault(appLogger)
logger.SetDefault(util.NewQuartzLogger(ctx))
Expand Down
5 changes: 3 additions & 2 deletions internal/server/handlers/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func (s *Service) readAllBackups(w http.ResponseWriter, r *http.Request, isFullB
return
}

response, err := dto.Serialize(dto.ConvertBackupDetailsMap(backups, s.config), dto.JSON)
response, err := dto.Serialize(dto.ConvertBackupDetailsMap(backups, s.config.BackupConfigCopy()), dto.JSON)
if err != nil {
hLogger.Error("failed to marshal backup list",
slog.Any("error", err),
Expand Down Expand Up @@ -169,8 +169,9 @@ func (s *Service) readBackupsForRoutine(w http.ResponseWriter, r *http.Request,
http.Error(w, "failed to retrieve backup list: "+err.Error(), http.StatusInternalServerError)
return
}
backupConfig := s.config.BackupConfigCopy()
backupDetails := dto.ConvertModelsToDTO(backups, func(m *model.BackupDetails) *dto.BackupDetails {
return dto.NewBackupDetailsFromModel(m, s.config)
return dto.NewBackupDetailsFromModel(m, backupConfig)
})
response, err := dto.Serialize(backupDetails, dto.JSON)
if err != nil {
Expand Down
23 changes: 8 additions & 15 deletions internal/server/handlers/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ func (s *Service) updateConfig(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

err = s.changeConfig(r.Context(), func(config *model.Config) error {
config.CopyFrom(newConfigModel)
config.SetBackupConfig(newConfigModel.BackupConfigCopy())
return nil
})

Expand Down Expand Up @@ -112,6 +113,7 @@ func (s *Service) ApplyConfig(w http.ResponseWriter, r *http.Request) {
return
}

// validate static fields.
newConfig := dto.NewConfigFromModel(s.config)
oldConfig := dto.NewConfigFromModel(config)
if err := validation.ValidateStaticFieldChanges(oldConfig, newConfig); err != nil {
Expand All @@ -123,7 +125,10 @@ func (s *Service) ApplyConfig(w http.ResponseWriter, r *http.Request) {
return
}

err = s.applyConfig(r.Context(), config)
backupConfig := config.BackupConfigCopy()
s.config.SetBackupConfig(backupConfig)
err = s.configApplier.ApplyNewRoutines(r.Context(), backupConfig.BackupRoutines)

if err != nil {
hLogger.Error("failed to apply config",
slog.Any("error", err),
Expand All @@ -136,9 +141,6 @@ func (s *Service) ApplyConfig(w http.ResponseWriter, r *http.Request) {
}

func (s *Service) changeConfig(ctx context.Context, updateFunc func(*model.Config) error) error {
s.Lock()
defer s.Unlock()

err := updateFunc(s.config)
if err != nil {
return fmt.Errorf("cannot update configuration: %w", err)
Expand All @@ -149,19 +151,10 @@ func (s *Service) changeConfig(ctx context.Context, updateFunc func(*model.Confi
return fmt.Errorf("failed to write configuration: %w", err)
}

err = s.configApplier.ApplyNewConfig(ctx)
err = s.configApplier.ApplyNewRoutines(ctx, s.config.Routines())
if err != nil {
return fmt.Errorf("failed to apply new configuration: %w", err)
}

return nil
}

func (s *Service) applyConfig(ctx context.Context, c *model.Config) error {
s.Lock()
defer s.Unlock()

s.config.CopyFrom(c)

return s.configApplier.ApplyNewConfig(ctx)
}
13 changes: 8 additions & 5 deletions internal/server/handlers/config_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,10 @@ func (s *Service) addAerospikeCluster(w http.ResponseWriter, r *http.Request) {
func (s *Service) ReadAerospikeClusters(w http.ResponseWriter, _ *http.Request) {
hLogger := s.logger.With(slog.String("handler", "ReadAerospikeClusters"))

toDTO := dto.ConvertModelMapToDTO(s.config.AerospikeClusters, func(m *model.AerospikeCluster) *dto.AerospikeCluster {
return dto.NewClusterFromModel(m, s.config)
backupConfig := s.config.BackupConfigCopy()
clusters := backupConfig.AerospikeClusters
toDTO := dto.ConvertModelMapToDTO(clusters, func(m *model.AerospikeCluster) *dto.AerospikeCluster {
return dto.NewClusterFromModel(m, backupConfig)
})
jsonResponse, err := dto.Serialize(toDTO, dto.JSON)
if err != nil {
Expand Down Expand Up @@ -132,15 +134,16 @@ func (s *Service) readAerospikeCluster(w http.ResponseWriter, r *http.Request) {
http.Error(w, clusterNameNotSpecifiedMsg, http.StatusBadRequest)
return
}
cluster, ok := s.config.AerospikeClusters[clusterName]
backupConfig := s.config.BackupConfigCopy()
cluster, ok := backupConfig.AerospikeClusters[clusterName]
if !ok {
hLogger.Error("cluster not found",
slog.String("name", clusterName),
)
http.Error(w, fmt.Sprintf("cluster %s could not be found", clusterName), http.StatusNotFound)
return
}
jsonResponse, err := dto.Serialize(dto.NewClusterFromModel(cluster, s.config), dto.JSON)
jsonResponse, err := dto.Serialize(dto.NewClusterFromModel(cluster, backupConfig), dto.JSON)
if err != nil {
hLogger.Error("failed to marshal cluster",
slog.Any("error", err),
Expand Down Expand Up @@ -194,7 +197,7 @@ func (s *Service) updateAerospikeCluster(w http.ResponseWriter, r *http.Request)
return
}

err = s.nsValidator.ValidateRoutines(cluster, s.config)
err = s.nsValidator.ValidateRoutines(cluster, s.config.Routines())
if err != nil {
hLogger.Error("cluster namespace validation failed",
slog.String("name", clusterName),
Expand Down
4 changes: 2 additions & 2 deletions internal/server/handlers/config_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (s *Service) addPolicy(w http.ResponseWriter, r *http.Request) {
func (s *Service) ReadPolicies(w http.ResponseWriter, _ *http.Request) {
hLogger := s.logger.With(slog.String("handler", "ReadPolicies"))

policies := dto.ConvertModelMapToDTO(s.config.BackupPolicies, dto.NewBackupPolicyFromModel)
policies := dto.ConvertModelMapToDTO(s.config.BackupConfigCopy().BackupPolicies, dto.NewBackupPolicyFromModel)
jsonResponse, err := dto.Serialize(policies, dto.JSON)
if err != nil {
hLogger.Error("failed to marshal backup policies",
Expand Down Expand Up @@ -121,7 +121,7 @@ func (s *Service) readPolicy(w http.ResponseWriter, r *http.Request) {
http.Error(w, policyNameNotSpecifiedMsg, http.StatusBadRequest)
return
}
policy, ok := s.config.BackupPolicies[policyName]
policy, ok := s.config.BackupConfigCopy().BackupPolicies[policyName]
if !ok {
hLogger.Error("policy not found")
http.Error(w, fmt.Sprintf("policy %s could not be found", policyName), http.StatusNotFound)
Expand Down
10 changes: 4 additions & 6 deletions internal/server/handlers/config_routine.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (s *Service) addRoutine(w http.ResponseWriter, r *http.Request) {
http.Error(w, routineNameNotSpecifiedMsg, http.StatusBadRequest)
return
}
toModel, err := newRoutine.ToModel(s.config, s.nsValidator)
toModel, err := newRoutine.ToModel(s.config.BackupConfigCopy(), s.nsValidator)
if err != nil {
hLogger.Error("failed to create routine",
slog.String("name", name),
Expand Down Expand Up @@ -91,7 +91,7 @@ func (s *Service) addRoutine(w http.ResponseWriter, r *http.Request) {
func (s *Service) ReadRoutines(w http.ResponseWriter, _ *http.Request) {
hLogger := s.logger.With(slog.String("handler", "ReadRoutines"))

toDTO := dto.ConvertModelMapToDTO(s.config.BackupRoutines, func(m *model.BackupRoutine) *dto.BackupRoutine {
toDTO := dto.ConvertModelMapToDTO(s.config.Routines(), func(m *model.BackupRoutine) *dto.BackupRoutine {
return dto.NewRoutineFromModel(m, s.config)
})

Expand Down Expand Up @@ -124,8 +124,6 @@ func (s *Service) ReadRoutines(w http.ResponseWriter, _ *http.Request) {
// @Success 200 {object} dto.BackupRoutine
// @Response 400 {string} string
// @Failure 404 {string} string "The specified cluster could not be found"
//
//nolint:dupl
func (s *Service) readRoutine(w http.ResponseWriter, r *http.Request) {
hLogger := s.logger.With(slog.String("handler", "readRoutine"))

Expand All @@ -135,7 +133,7 @@ func (s *Service) readRoutine(w http.ResponseWriter, r *http.Request) {
http.Error(w, routineNameNotSpecifiedMsg, http.StatusBadRequest)
return
}
routine, ok := s.config.BackupRoutines[routineName]
routine, ok := s.config.Routines()[routineName]
if !ok {
http.Error(w, fmt.Sprintf("Routine %s could not be found", routineName), http.StatusNotFound)
return
Expand Down Expand Up @@ -190,7 +188,7 @@ func (s *Service) updateRoutine(w http.ResponseWriter, r *http.Request) {
return
}

toModel, err := updatedRoutine.ToModel(s.config, s.nsValidator)
toModel, err := updatedRoutine.ToModel(s.config.BackupConfigCopy(), s.nsValidator)
if err != nil {
hLogger.Error("failed to create routine",
slog.String("name", name),
Expand Down
10 changes: 5 additions & 5 deletions internal/server/handlers/config_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ func (s *Service) addStorage(w http.ResponseWriter, r *http.Request) {
func (s *Service) ReadAllStorage(w http.ResponseWriter, _ *http.Request) {
hLogger := s.logger.With(slog.String("handler", "ReadAllStorage"))

toDTO := dto.ConvertStorageMapToDTO(s.config.Storage, s.config)
backupConfig := s.config.BackupConfigCopy()
toDTO := dto.ConvertStorageMapToDTO(backupConfig.Storage, backupConfig)
jsonResponse, err := dto.Serialize(toDTO, dto.JSON)
if err != nil {
hLogger.Error("failed to marshal storage",
Expand Down Expand Up @@ -113,8 +114,6 @@ func (s *Service) ReadAllStorage(w http.ResponseWriter, _ *http.Request) {
// @Response 400 {string} string
// @Failure 404 {string} string "The specified storage could not be found"
// @Failure 500 {string} string
//
//nolint:dupl
func (s *Service) readStorage(w http.ResponseWriter, r *http.Request) {
hLogger := s.logger.With(slog.String("handler", "readStorage"))

Expand All @@ -124,13 +123,14 @@ func (s *Service) readStorage(w http.ResponseWriter, r *http.Request) {
http.Error(w, storageNameNotSpecifiedMsg, http.StatusBadRequest)
return
}
storage, ok := s.config.Storage[storageName]
backupConfig := s.config.BackupConfigCopy()
storage, ok := backupConfig.Storage[storageName]
if !ok {
http.Error(w, fmt.Sprintf("Storage %s could not be found", storageName), http.StatusNotFound)
return
}

jsonResponse, err := dto.Serialize(dto.NewStorageFromModel(storage, s.config), dto.JSON)
jsonResponse, err := dto.Serialize(dto.NewStorageFromModel(storage, backupConfig), dto.JSON)
if err != nil {
hLogger.Error("failed to marshal storage",
slog.Any("error", err),
Expand Down
6 changes: 3 additions & 3 deletions internal/server/handlers/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ func (mock backendsHolderMock) Get(routineName string) (*service.BackupBackend,
return &service.BackupBackend{}, true
}

func (mock backendsHolderMock) Init(_ *model.Config) {
func (mock backendsHolderMock) Init(_ map[string]*model.BackupRoutine) {
}

func (mock backendsHolderMock) GetAllReaders() map[string]service.BackupListReader {
Expand All @@ -209,7 +209,7 @@ func (mock configurationManagerMock) Write(_ context.Context, config *model.Conf

type MockConfigApplier struct{}

func (a *MockConfigApplier) ApplyNewConfig(context.Context) error {
func (a *MockConfigApplier) ApplyNewRoutines(_ context.Context, _ map[string]*model.BackupRoutine) error {
return nil
}

Expand All @@ -219,7 +219,7 @@ func (m *MockNamespaceValidator) MissingNamespaces(_ *model.AerospikeCluster, _
return nil
}

func (m *MockNamespaceValidator) ValidateRoutines(_ *model.AerospikeCluster, _ *model.Config) error {
func (m *MockNamespaceValidator) ValidateRoutines(_ *model.AerospikeCluster, _ map[string]*model.BackupRoutine) error {
return nil
}

Expand Down
2 changes: 0 additions & 2 deletions internal/server/handlers/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package handlers

import (
"log/slog"
"sync"

"github.com/aerospike/aerospike-backup-service/v3/internal/server/configuration"
"github.com/aerospike/aerospike-backup-service/v3/pkg/model"
Expand All @@ -12,7 +11,6 @@ import (
)

type Service struct {
sync.Mutex
config *model.Config
configApplier service.ConfigApplier
scheduler quartz.Scheduler
Expand Down
6 changes: 3 additions & 3 deletions pkg/dto/aerospike_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func NewClusterFromReader(r io.Reader, format SerializationFormat) (*AerospikeCl
return a, nil
}

func NewClusterFromModel(m *model.AerospikeCluster, config *model.Config) *AerospikeCluster {
func NewClusterFromModel(m *model.AerospikeCluster, config *model.BackupConfig) *AerospikeCluster {
if m == nil {
return nil
}
Expand All @@ -73,7 +73,7 @@ func NewClusterFromModel(m *model.AerospikeCluster, config *model.Config) *Aeros
return a
}

func (a *AerospikeCluster) fromModel(m *model.AerospikeCluster, config *model.Config) {
func (a *AerospikeCluster) fromModel(m *model.AerospikeCluster, config *model.BackupConfig) {
a.ClusterLabel = m.ClusterLabel
a.SeedNodes = make([]SeedNode, len(m.SeedNodes))
for i, v := range m.SeedNodes {
Expand Down Expand Up @@ -181,7 +181,7 @@ type Credentials struct {
AuthMode *string `yaml:"auth-mode,omitempty" json:"auth-mode,omitempty" enums:"INTERNAL,EXTERNAL,PKI"`
}

func (c *Credentials) fromModel(m *model.Credentials, config *model.Config) {
func (c *Credentials) fromModel(m *model.Credentials, config *model.BackupConfig) {
c.User = m.User
c.Password = m.Password
c.PasswordPath = m.PasswordPath
Expand Down
26 changes: 13 additions & 13 deletions pkg/dto/backup_details.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,18 @@ type BackupMetadata struct {
UDFCount uint64 `yaml:"udf-count" json:"udf-count" format:"int64" example:"2"`
}

func (d *BackupDetails) fromModel(m *model.BackupDetails, config *model.Config) {
// NewBackupDetailsFromModel creates a new BackupDetails from a model.BackupDetails
func NewBackupDetailsFromModel(m *model.BackupDetails, config *model.BackupConfig) *BackupDetails {
if m == nil {
return nil
}

var d BackupDetails
d.fromModel(m, config)
return &d
}

func (d *BackupDetails) fromModel(m *model.BackupDetails, config *model.BackupConfig) {
d.Key = m.Key
d.Created = m.Created
d.From = m.From
Expand All @@ -48,19 +59,8 @@ func (d *BackupDetails) fromModel(m *model.BackupDetails, config *model.Config)
d.Storage = NewStorageFromModel(m.Storage, config)
}

// NewBackupDetailsFromModel creates a new BackupDetails from a model.BackupDetails
func NewBackupDetailsFromModel(m *model.BackupDetails, config *model.Config) *BackupDetails {
if m == nil {
return nil
}

var d BackupDetails
d.fromModel(m, config)
return &d
}

func ConvertBackupDetailsMap(
modelMap map[string][]model.BackupDetails, config *model.Config,
modelMap map[string][]model.BackupDetails, config *model.BackupConfig,
) map[string][]BackupDetails {
result := make(map[string][]BackupDetails, len(modelMap))
for key, modelSlice := range modelMap {
Expand Down
Loading
Loading