diff --git a/api/router.go b/api/router.go index 3cd7d427..ef31d68e 100644 --- a/api/router.go +++ b/api/router.go @@ -77,7 +77,7 @@ func Router(c *ctx.AptlyContext) http.Handler { } if c.Config().ServeInAPIMode { - router.GET("/repos/", reposListInAPIMode(c.Config().FileSystemPublishRoots)) + router.GET("/repos/", reposListInAPIMode(c.Config().GetFileSystemPublishRoots())) router.GET("/repos/:storage/*pkgPath", reposServeInAPIMode) } diff --git a/api/s3.go b/api/s3.go index f38b0847..e5ebda29 100644 --- a/api/s3.go +++ b/api/s3.go @@ -14,7 +14,9 @@ import ( // @Router /api/s3 [get] func apiS3List(c *gin.Context) { keys := []string{} - for k := range context.Config().S3PublishRoots { + // Use safe accessor to get a copy of the map + s3Roots := context.Config().GetS3PublishRoots() + for k := range s3Roots { keys = append(keys, k) } c.JSON(200, keys) diff --git a/context/context.go b/context/context.go index 0ffc3f72..3528f52e 100644 --- a/context/context.go +++ b/context/context.go @@ -416,14 +416,18 @@ func (context *AptlyContext) GetPublishedStorage(name string) aptly.PublishedSto if name == "" { publishedStorage = files.NewPublishedStorage(filepath.Join(context.config().GetRootDir(), "public"), "hardlink", "") } else if strings.HasPrefix(name, "filesystem:") { - params, ok := context.config().FileSystemPublishRoots[name[11:]] + // Get a safe copy of the map + fileSystemRoots := context.config().GetFileSystemPublishRoots() + params, ok := fileSystemRoots[name[11:]] if !ok { Fatal(fmt.Errorf("published local storage %v not configured", name[11:])) } publishedStorage = files.NewPublishedStorage(params.RootDir, params.LinkMethod, params.VerifyMethod) } else if strings.HasPrefix(name, "s3:") { - params, ok := context.config().S3PublishRoots[name[3:]] + // Get a safe copy of the map + s3Roots := context.config().GetS3PublishRoots() + params, ok := s3Roots[name[3:]] if !ok { Fatal(fmt.Errorf("published S3 storage %v not configured", name[3:])) } @@ -438,7 +442,9 @@ func (context *AptlyContext) GetPublishedStorage(name string) aptly.PublishedSto Fatal(err) } } else if strings.HasPrefix(name, "swift:") { - params, ok := context.config().SwiftPublishRoots[name[6:]] + // Get a safe copy of the map + swiftRoots := context.config().GetSwiftPublishRoots() + params, ok := swiftRoots[name[6:]] if !ok { Fatal(fmt.Errorf("published Swift storage %v not configured", name[6:])) } @@ -450,7 +456,9 @@ func (context *AptlyContext) GetPublishedStorage(name string) aptly.PublishedSto Fatal(err) } } else if strings.HasPrefix(name, "azure:") { - params, ok := context.config().AzurePublishRoots[name[6:]] + // Get a safe copy of the map + azureRoots := context.config().GetAzurePublishRoots() + params, ok := azureRoots[name[6:]] if !ok { Fatal(fmt.Errorf("published Azure storage %v not configured", name[6:])) } diff --git a/utils/config.go b/utils/config.go index 4cfac039..b979bc39 100644 --- a/utils/config.go +++ b/utils/config.go @@ -324,3 +324,39 @@ func SaveConfigYAML(filename string, config *ConfigStructure) error { func (conf *ConfigStructure) GetRootDir() string { return strings.Replace(conf.RootDir, "~", os.Getenv("HOME"), 1) } + +// GetFileSystemPublishRoots returns a copy of FileSystemPublishRoots map to avoid concurrent access +func (conf *ConfigStructure) GetFileSystemPublishRoots() map[string]FileSystemPublishRoot { + result := make(map[string]FileSystemPublishRoot, len(conf.FileSystemPublishRoots)) + for k, v := range conf.FileSystemPublishRoots { + result[k] = v + } + return result +} + +// GetS3PublishRoots returns a copy of S3PublishRoots map to avoid concurrent access +func (conf *ConfigStructure) GetS3PublishRoots() map[string]S3PublishRoot { + result := make(map[string]S3PublishRoot, len(conf.S3PublishRoots)) + for k, v := range conf.S3PublishRoots { + result[k] = v + } + return result +} + +// GetSwiftPublishRoots returns a copy of SwiftPublishRoots map to avoid concurrent access +func (conf *ConfigStructure) GetSwiftPublishRoots() map[string]SwiftPublishRoot { + result := make(map[string]SwiftPublishRoot, len(conf.SwiftPublishRoots)) + for k, v := range conf.SwiftPublishRoots { + result[k] = v + } + return result +} + +// GetAzurePublishRoots returns a copy of AzurePublishRoots map to avoid concurrent access +func (conf *ConfigStructure) GetAzurePublishRoots() map[string]AzureEndpoint { + result := make(map[string]AzureEndpoint, len(conf.AzurePublishRoots)) + for k, v := range conf.AzurePublishRoots { + result[k] = v + } + return result +}