@@ -10,7 +10,6 @@ import (
10
10
11
11
"zotregistry.dev/zot/pkg/compat"
12
12
extconf "zotregistry.dev/zot/pkg/extensions/config"
13
- syncconf "zotregistry.dev/zot/pkg/extensions/config/sync"
14
13
storageConstants "zotregistry.dev/zot/pkg/storage/constants"
15
14
)
16
15
@@ -287,20 +286,32 @@ type GlobalStorageConfig struct {
287
286
}
288
287
289
288
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"`
292
291
AdminPolicy Policy
293
292
Groups Groups
294
293
Metrics Metrics
295
294
}
296
295
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
+
297
308
// IsAuthzEnabled checks if authorization is enabled (access control is configured).
298
309
func (config * AccessControlConfig ) IsAuthzEnabled () bool {
299
310
return config != nil
300
311
}
301
312
302
313
func (config * AccessControlConfig ) AnonymousPolicyExists () bool {
303
- if config == nil {
314
+ if config == nil || config . mu == nil {
304
315
return false
305
316
}
306
317
@@ -318,7 +329,7 @@ func (config *AccessControlConfig) AnonymousPolicyExists() bool {
318
329
319
330
// ContainsOnlyAnonymousPolicy checks if the access control configuration contains only anonymous policies.
320
331
func (config * AccessControlConfig ) ContainsOnlyAnonymousPolicy () bool {
321
- if config == nil {
332
+ if config == nil || config . mu == nil {
322
333
return true
323
334
}
324
335
@@ -356,7 +367,7 @@ func (config *AccessControlConfig) ContainsOnlyAnonymousPolicy() bool {
356
367
357
368
// SetRepositories safely sets the repositories configuration.
358
369
func (config * AccessControlConfig ) SetRepositories (repositories Repositories ) {
359
- if config == nil {
370
+ if config == nil || config . mu == nil {
360
371
return
361
372
}
362
373
@@ -368,7 +379,7 @@ func (config *AccessControlConfig) SetRepositories(repositories Repositories) {
368
379
369
380
// SetAdminPolicy safely sets the admin policy.
370
381
func (config * AccessControlConfig ) SetAdminPolicy (policy Policy ) {
371
- if config == nil {
382
+ if config == nil || config . mu == nil {
372
383
return
373
384
}
374
385
@@ -380,7 +391,7 @@ func (config *AccessControlConfig) SetAdminPolicy(policy Policy) {
380
391
381
392
// SetMetrics safely sets the metrics configuration.
382
393
func (config * AccessControlConfig ) SetMetrics (metrics Metrics ) {
383
- if config == nil {
394
+ if config == nil || config . mu == nil {
384
395
return
385
396
}
386
397
@@ -392,7 +403,7 @@ func (config *AccessControlConfig) SetMetrics(metrics Metrics) {
392
403
393
404
// SetGroups safely sets the groups configuration.
394
405
func (config * AccessControlConfig ) SetGroups (groups Groups ) {
395
- if config == nil {
406
+ if config == nil || config . mu == nil {
396
407
return
397
408
}
398
409
@@ -404,7 +415,7 @@ func (config *AccessControlConfig) SetGroups(groups Groups) {
404
415
405
416
// GetRepositories safely gets a copy of the repositories configuration.
406
417
func (config * AccessControlConfig ) GetRepositories () Repositories {
407
- if config == nil {
418
+ if config == nil || config . mu == nil {
408
419
return nil
409
420
}
410
421
@@ -422,7 +433,7 @@ func (config *AccessControlConfig) GetRepositories() Repositories {
422
433
423
434
// GetAdminPolicy safely gets a copy of the admin policy.
424
435
func (config * AccessControlConfig ) GetAdminPolicy () Policy {
425
- if config == nil {
436
+ if config == nil || config . mu == nil {
426
437
return Policy {}
427
438
}
428
439
@@ -434,7 +445,7 @@ func (config *AccessControlConfig) GetAdminPolicy() Policy {
434
445
435
446
// GetMetrics safely gets a copy of the metrics configuration.
436
447
func (config * AccessControlConfig ) GetMetrics () Metrics {
437
- if config == nil {
448
+ if config == nil || config . mu == nil {
438
449
return Metrics {}
439
450
}
440
451
@@ -446,7 +457,7 @@ func (config *AccessControlConfig) GetMetrics() Metrics {
446
457
447
458
// GetGroups safely gets a copy of the groups configuration.
448
459
func (config * AccessControlConfig ) GetGroups () Groups {
449
- if config == nil {
460
+ if config == nil || config . mu == nil {
450
461
return nil
451
462
}
452
463
@@ -725,129 +736,92 @@ func (c *Config) UpdateReloadableConfig(newConfig *Config) {
725
736
}
726
737
727
738
c .mu .Lock ()
739
+ defer c .mu .Unlock ()
728
740
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
770
742
c .Storage .GC = newConfig .Storage .GC
771
743
c .Storage .Dedupe = newConfig .Storage .Dedupe
772
744
c .Storage .GCDelay = newConfig .Storage .GCDelay
773
745
c .Storage .GCInterval = newConfig .Storage .GCInterval
774
746
775
- // Only if we have a metaDB already in place
747
+ // Only update retention if we have a metaDB already in place
776
748
if c .isRetentionEnabledInternal () {
777
749
c .Storage .Retention = newConfig .Storage .Retention
778
750
}
779
751
780
- // Update subpaths
752
+ // Update storage subpaths configuration
781
753
for subPath , storageConfig := range newConfig .Storage .SubPaths {
782
754
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
793
757
}
794
- }
795
758
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
799
770
}
800
771
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
804
778
}
805
779
806
- // Initialize AccessControlConfig if needed while holding the main lock
780
+ // Initialize and update AccessControlConfig
807
781
if newConfig .HTTP .AccessControl != nil && c .HTTP .AccessControl == nil {
808
782
c .HTTP .AccessControl = & AccessControlConfig {}
783
+ c .HTTP .AccessControl .SetMutex (c .GetMutex ())
809
784
}
810
785
811
- // Set AccessControlConfig to nil if new config doesn't have it
812
786
if newConfig .HTTP .AccessControl == nil {
813
787
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
814
794
}
815
795
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 ())
824
800
}
825
801
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
835
807
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
+ }
839
816
} else {
840
817
// 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
+ }
842
821
}
843
822
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
851
825
}
852
826
}
853
827
@@ -875,19 +849,24 @@ func (c *Config) GetAuthConfig() *AuthConfig {
875
849
}
876
850
877
851
// 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.
879
853
func (c * Config ) GetAccessControlConfig () * AccessControlConfig {
880
854
if c == nil {
881
855
return nil
882
856
}
883
857
884
- c .mu .RLock ()
885
- defer c .mu .RUnlock ()
858
+ c .mu .Lock ()
859
+ defer c .mu .Unlock ()
886
860
887
861
if c .HTTP .AccessControl == nil {
888
862
return nil
889
863
}
890
864
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
+
891
870
return c .HTTP .AccessControl
892
871
}
893
872
@@ -922,19 +901,24 @@ func (c *Config) GetStorageConfig() GlobalStorageConfig {
922
901
}
923
902
924
903
// 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.
926
905
func (c * Config ) GetExtensionsConfig () * extconf.ExtensionConfig {
927
906
if c == nil {
928
907
return nil
929
908
}
930
909
931
- c .mu .RLock ()
932
- defer c .mu .RUnlock ()
910
+ c .mu .Lock ()
911
+ defer c .mu .Unlock ()
933
912
934
913
if c .Extensions == nil {
935
914
return nil
936
915
}
937
916
917
+ // Ensure the mutex reference is set (thread-safe check)
918
+ if ! c .Extensions .IsMutexSet () {
919
+ c .Extensions .SetMutex (c .GetMutex ())
920
+ }
921
+
938
922
return c .Extensions
939
923
}
940
924
@@ -1076,6 +1060,15 @@ func (c *Config) IsCompatEnabled() bool {
1076
1060
return len (c .HTTP .Compat ) > 0
1077
1061
}
1078
1062
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
+
1079
1072
// =============================================================================
1080
1073
// HELPER FUNCTIONS
1081
1074
// =============================================================================
0 commit comments