Skip to content

Commit a4432ac

Browse files
committed
fix: Config, AccessControlConfig and ExtensionsConfig to use the same mutex
Also solve an issue with config reload ignoring api key and openid settings Signed-off-by: Andrei Aaron <[email protected]>
1 parent 2169e0a commit a4432ac

File tree

10 files changed

+504
-325
lines changed

10 files changed

+504
-325
lines changed

pkg/api/config/config.go

Lines changed: 102 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010

1111
"zotregistry.dev/zot/pkg/compat"
1212
extconf "zotregistry.dev/zot/pkg/extensions/config"
13-
syncconf "zotregistry.dev/zot/pkg/extensions/config/sync"
1413
storageConstants "zotregistry.dev/zot/pkg/storage/constants"
1514
)
1615

@@ -287,20 +286,32 @@ type GlobalStorageConfig struct {
287286
}
288287

289288
type AccessControlConfig struct {
290-
mu sync.RWMutex
291-
Repositories Repositories `json:"repositories" mapstructure:"repositories"`
289+
mu *sync.RWMutex `json:"-"` // Reference to parent Config's mutex (excluded from JSON)
290+
Repositories Repositories `json:"repositories" mapstructure:"repositories"`
292291
AdminPolicy Policy
293292
Groups Groups
294293
Metrics Metrics
295294
}
296295

296+
// SetMutex sets the mutex reference for this AccessControlConfig.
297+
func (config *AccessControlConfig) SetMutex(mu *sync.RWMutex) {
298+
if config != nil {
299+
config.mu = mu
300+
}
301+
}
302+
303+
// IsMutexSet checks if the mutex reference is set for this AccessControlConfig.
304+
func (config *AccessControlConfig) IsMutexSet() bool {
305+
return config != nil && config.mu != nil
306+
}
307+
297308
// IsAuthzEnabled checks if authorization is enabled (access control is configured).
298309
func (config *AccessControlConfig) IsAuthzEnabled() bool {
299310
return config != nil
300311
}
301312

302313
func (config *AccessControlConfig) AnonymousPolicyExists() bool {
303-
if config == nil {
314+
if config == nil || config.mu == nil {
304315
return false
305316
}
306317

@@ -318,7 +329,7 @@ func (config *AccessControlConfig) AnonymousPolicyExists() bool {
318329

319330
// ContainsOnlyAnonymousPolicy checks if the access control configuration contains only anonymous policies.
320331
func (config *AccessControlConfig) ContainsOnlyAnonymousPolicy() bool {
321-
if config == nil {
332+
if config == nil || config.mu == nil {
322333
return true
323334
}
324335

@@ -356,7 +367,7 @@ func (config *AccessControlConfig) ContainsOnlyAnonymousPolicy() bool {
356367

357368
// SetRepositories safely sets the repositories configuration.
358369
func (config *AccessControlConfig) SetRepositories(repositories Repositories) {
359-
if config == nil {
370+
if config == nil || config.mu == nil {
360371
return
361372
}
362373

@@ -368,7 +379,7 @@ func (config *AccessControlConfig) SetRepositories(repositories Repositories) {
368379

369380
// SetAdminPolicy safely sets the admin policy.
370381
func (config *AccessControlConfig) SetAdminPolicy(policy Policy) {
371-
if config == nil {
382+
if config == nil || config.mu == nil {
372383
return
373384
}
374385

@@ -380,7 +391,7 @@ func (config *AccessControlConfig) SetAdminPolicy(policy Policy) {
380391

381392
// SetMetrics safely sets the metrics configuration.
382393
func (config *AccessControlConfig) SetMetrics(metrics Metrics) {
383-
if config == nil {
394+
if config == nil || config.mu == nil {
384395
return
385396
}
386397

@@ -392,7 +403,7 @@ func (config *AccessControlConfig) SetMetrics(metrics Metrics) {
392403

393404
// SetGroups safely sets the groups configuration.
394405
func (config *AccessControlConfig) SetGroups(groups Groups) {
395-
if config == nil {
406+
if config == nil || config.mu == nil {
396407
return
397408
}
398409

@@ -404,7 +415,7 @@ func (config *AccessControlConfig) SetGroups(groups Groups) {
404415

405416
// GetRepositories safely gets a copy of the repositories configuration.
406417
func (config *AccessControlConfig) GetRepositories() Repositories {
407-
if config == nil {
418+
if config == nil || config.mu == nil {
408419
return nil
409420
}
410421

@@ -422,7 +433,7 @@ func (config *AccessControlConfig) GetRepositories() Repositories {
422433

423434
// GetAdminPolicy safely gets a copy of the admin policy.
424435
func (config *AccessControlConfig) GetAdminPolicy() Policy {
425-
if config == nil {
436+
if config == nil || config.mu == nil {
426437
return Policy{}
427438
}
428439

@@ -434,7 +445,7 @@ func (config *AccessControlConfig) GetAdminPolicy() Policy {
434445

435446
// GetMetrics safely gets a copy of the metrics configuration.
436447
func (config *AccessControlConfig) GetMetrics() Metrics {
437-
if config == nil {
448+
if config == nil || config.mu == nil {
438449
return Metrics{}
439450
}
440451

@@ -446,7 +457,7 @@ func (config *AccessControlConfig) GetMetrics() Metrics {
446457

447458
// GetGroups safely gets a copy of the groups configuration.
448459
func (config *AccessControlConfig) GetGroups() Groups {
449-
if config == nil {
460+
if config == nil || config.mu == nil {
450461
return nil
451462
}
452463

@@ -725,129 +736,92 @@ func (c *Config) UpdateReloadableConfig(newConfig *Config) {
725736
}
726737

727738
c.mu.Lock()
739+
defer c.mu.Unlock()
728740

729-
// Capture values while holding the main lock
730-
var (
731-
newRepositories Repositories
732-
newAdminPolicy Policy
733-
newMetrics Metrics
734-
newGroups Groups
735-
newSyncConfig *syncconf.Config
736-
newSearchCVEConfig *extconf.CVEConfig
737-
newScrubConfig *extconf.ScrubConfig
738-
)
739-
740-
if newConfig.HTTP.AccessControl != nil {
741-
// Access fields directly to avoid potential deadlock from calling getter methods
742-
// while holding the main Config lock
743-
newRepositories = newConfig.HTTP.AccessControl.Repositories
744-
newAdminPolicy = newConfig.HTTP.AccessControl.AdminPolicy
745-
newMetrics = newConfig.HTTP.AccessControl.Metrics
746-
newGroups = newConfig.HTTP.AccessControl.Groups
747-
}
748-
749-
if newConfig.Extensions != nil {
750-
if newConfig.Extensions.Sync != nil {
751-
newSyncConfig = newConfig.Extensions.Sync
752-
}
753-
754-
if newConfig.Extensions.Search != nil && newConfig.Extensions.Search.CVE != nil {
755-
newSearchCVEConfig = newConfig.Extensions.Search.CVE
756-
}
757-
758-
if newConfig.Extensions.Scrub != nil {
759-
newScrubConfig = newConfig.Extensions.Scrub
760-
}
761-
}
762-
763-
// Update basic fields while holding the main lock
764-
if c.HTTP.Auth != nil {
765-
c.HTTP.Auth.HTPasswd = newConfig.HTTP.Auth.HTPasswd
766-
c.HTTP.Auth.LDAP = newConfig.HTTP.Auth.LDAP
767-
}
768-
769-
// Update storage config
741+
// Update storage configuration
770742
c.Storage.GC = newConfig.Storage.GC
771743
c.Storage.Dedupe = newConfig.Storage.Dedupe
772744
c.Storage.GCDelay = newConfig.Storage.GCDelay
773745
c.Storage.GCInterval = newConfig.Storage.GCInterval
774746

775-
// Only if we have a metaDB already in place
747+
// Only update retention if we have a metaDB already in place
776748
if c.isRetentionEnabledInternal() {
777749
c.Storage.Retention = newConfig.Storage.Retention
778750
}
779751

780-
// Update subpaths
752+
// Update storage subpaths configuration
781753
for subPath, storageConfig := range newConfig.Storage.SubPaths {
782754
subPathConfig, ok := c.Storage.SubPaths[subPath]
783-
if ok {
784-
subPathConfig.GC = storageConfig.GC
785-
subPathConfig.Dedupe = storageConfig.Dedupe
786-
subPathConfig.GCDelay = storageConfig.GCDelay
787-
subPathConfig.GCInterval = storageConfig.GCInterval
788-
// only if we have a metaDB already in place
789-
if c.isRetentionEnabledInternal() {
790-
subPathConfig.Retention = storageConfig.Retention
791-
}
792-
c.Storage.SubPaths[subPath] = subPathConfig
755+
if !ok {
756+
continue
793757
}
794-
}
795758

796-
// Initialize ExtensionConfig if needed while holding the main lock
797-
if newConfig.Extensions != nil && c.Extensions == nil {
798-
c.Extensions = &extconf.ExtensionConfig{}
759+
subPathConfig.GC = storageConfig.GC
760+
subPathConfig.Dedupe = storageConfig.Dedupe
761+
subPathConfig.GCDelay = storageConfig.GCDelay
762+
subPathConfig.GCInterval = storageConfig.GCInterval
763+
764+
// Only update retention if we have a metaDB already in place
765+
if c.isRetentionEnabledInternal() {
766+
subPathConfig.Retention = storageConfig.Retention
767+
}
768+
769+
c.Storage.SubPaths[subPath] = subPathConfig
799770
}
800771

801-
// Set ExtensionConfig to nil if new config doesn't have it
802-
if newConfig.Extensions == nil {
803-
c.Extensions = nil
772+
// Update authentication configuration
773+
if c.HTTP.Auth != nil {
774+
c.HTTP.Auth.HTPasswd = newConfig.HTTP.Auth.HTPasswd
775+
c.HTTP.Auth.LDAP = newConfig.HTTP.Auth.LDAP
776+
c.HTTP.Auth.APIKey = newConfig.HTTP.Auth.APIKey
777+
c.HTTP.Auth.OpenID = newConfig.HTTP.Auth.OpenID
804778
}
805779

806-
// Initialize AccessControlConfig if needed while holding the main lock
780+
// Initialize and update AccessControlConfig
807781
if newConfig.HTTP.AccessControl != nil && c.HTTP.AccessControl == nil {
808782
c.HTTP.AccessControl = &AccessControlConfig{}
783+
c.HTTP.AccessControl.SetMutex(c.GetMutex())
809784
}
810785

811-
// Set AccessControlConfig to nil if new config doesn't have it
812786
if newConfig.HTTP.AccessControl == nil {
813787
c.HTTP.AccessControl = nil
788+
} else {
789+
// Update AccessControlConfig fields directly
790+
c.HTTP.AccessControl.Repositories = newConfig.HTTP.AccessControl.Repositories
791+
c.HTTP.AccessControl.AdminPolicy = newConfig.HTTP.AccessControl.AdminPolicy
792+
c.HTTP.AccessControl.Metrics = newConfig.HTTP.AccessControl.Metrics
793+
c.HTTP.AccessControl.Groups = newConfig.HTTP.AccessControl.Groups
814794
}
815795

816-
c.mu.Unlock() // Release the main lock before calling setter methods
817-
818-
// Now update AccessControlConfig using its own thread-safe methods
819-
if newConfig.HTTP.AccessControl != nil {
820-
c.HTTP.AccessControl.SetRepositories(newRepositories)
821-
c.HTTP.AccessControl.SetAdminPolicy(newAdminPolicy)
822-
c.HTTP.AccessControl.SetMetrics(newMetrics)
823-
c.HTTP.AccessControl.SetGroups(newGroups)
796+
// Initialize and update ExtensionConfig
797+
if newConfig.Extensions != nil && c.Extensions == nil {
798+
c.Extensions = &extconf.ExtensionConfig{}
799+
c.Extensions.SetMutex(c.GetMutex())
824800
}
825801

826-
// Now update ExtensionConfig using its own thread-safe methods
827-
if newConfig.Extensions != nil && c.Extensions != nil {
828-
// reload sync extension
829-
if newSyncConfig != nil {
830-
c.Extensions.SetSyncConfig(newSyncConfig)
831-
} else {
832-
// Remove sync extension if not present in new config
833-
c.Extensions.SetSyncConfig(nil)
834-
}
802+
if newConfig.Extensions == nil {
803+
c.Extensions = nil
804+
} else if c.Extensions != nil {
805+
// Update sync extension
806+
c.Extensions.Sync = newConfig.Extensions.Sync
835807

836-
// reload only if search is enabled and reloaded config has search extension
837-
if c.isSearchEnabledInternal() && newSearchCVEConfig != nil {
838-
c.Extensions.SetSearchCVEConfig(newSearchCVEConfig)
808+
// Update search extension
809+
if newConfig.Extensions.Search != nil && newConfig.Extensions.Search.CVE != nil {
810+
// Only update if search is enabled
811+
if c.isSearchEnabledInternal() {
812+
if c.Extensions.Search != nil {
813+
c.Extensions.Search.CVE = newConfig.Extensions.Search.CVE
814+
}
815+
}
839816
} else {
840817
// Remove search CVE config if not present in new config
841-
c.Extensions.SetSearchCVEConfig(nil)
818+
if c.Extensions.Search != nil {
819+
c.Extensions.Search.CVE = nil
820+
}
842821
}
843822

844-
// reload scrub extension
845-
if newScrubConfig != nil {
846-
c.Extensions.SetScrubConfig(newScrubConfig)
847-
} else {
848-
// Remove scrub extension if not present in new config
849-
c.Extensions.SetScrubConfig(nil)
850-
}
823+
// Update scrub extension
824+
c.Extensions.Scrub = newConfig.Extensions.Scrub
851825
}
852826
}
853827

@@ -875,19 +849,24 @@ func (c *Config) GetAuthConfig() *AuthConfig {
875849
}
876850

877851
// GetAccessControlConfig returns the access control config if it exists
878-
// The returned AccessControlConfig has its own mutex for thread-safety.
852+
// The returned AccessControlConfig shares the parent Config's mutex for thread-safety.
879853
func (c *Config) GetAccessControlConfig() *AccessControlConfig {
880854
if c == nil {
881855
return nil
882856
}
883857

884-
c.mu.RLock()
885-
defer c.mu.RUnlock()
858+
c.mu.Lock()
859+
defer c.mu.Unlock()
886860

887861
if c.HTTP.AccessControl == nil {
888862
return nil
889863
}
890864

865+
// Ensure the mutex reference is set (thread-safe check)
866+
if !c.HTTP.AccessControl.IsMutexSet() {
867+
c.HTTP.AccessControl.SetMutex(c.GetMutex())
868+
}
869+
891870
return c.HTTP.AccessControl
892871
}
893872

@@ -922,19 +901,24 @@ func (c *Config) GetStorageConfig() GlobalStorageConfig {
922901
}
923902

924903
// GetExtensionsConfig returns the extensions config if it exists.
925-
// The returned ExtensionConfig has its own mutex for thread-safety.
904+
// The returned ExtensionConfig shares the parent Config's mutex for thread-safety.
926905
func (c *Config) GetExtensionsConfig() *extconf.ExtensionConfig {
927906
if c == nil {
928907
return nil
929908
}
930909

931-
c.mu.RLock()
932-
defer c.mu.RUnlock()
910+
c.mu.Lock()
911+
defer c.mu.Unlock()
933912

934913
if c.Extensions == nil {
935914
return nil
936915
}
937916

917+
// Ensure the mutex reference is set (thread-safe check)
918+
if !c.Extensions.IsMutexSet() {
919+
c.Extensions.SetMutex(c.GetMutex())
920+
}
921+
938922
return c.Extensions
939923
}
940924

@@ -1076,6 +1060,15 @@ func (c *Config) IsCompatEnabled() bool {
10761060
return len(c.HTTP.Compat) > 0
10771061
}
10781062

1063+
// GetMutex returns a reference to the Config's mutex.
1064+
func (c *Config) GetMutex() *sync.RWMutex {
1065+
if c != nil {
1066+
return &c.mu
1067+
}
1068+
1069+
return nil
1070+
}
1071+
10791072
// =============================================================================
10801073
// HELPER FUNCTIONS
10811074
// =============================================================================

0 commit comments

Comments
 (0)