mirror of
https://github.com/turbot/steampipe.git
synced 2025-12-19 18:12:43 -05:00
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/turbot/pipe-fittings/v2/utils"
|
"github.com/turbot/pipe-fittings/v2/utils"
|
||||||
"github.com/turbot/steampipe-plugin-sdk/v5/sperr"
|
"github.com/turbot/steampipe-plugin-sdk/v5/sperr"
|
||||||
@@ -17,6 +18,7 @@ import (
|
|||||||
type Manager struct {
|
type Manager struct {
|
||||||
registeredExporters map[string]Exporter
|
registeredExporters map[string]Exporter
|
||||||
registeredExtensions map[string]Exporter
|
registeredExtensions map[string]Exporter
|
||||||
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewManager() *Manager {
|
func NewManager() *Manager {
|
||||||
@@ -27,6 +29,9 @@ func NewManager() *Manager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) Register(exporter Exporter) error {
|
func (m *Manager) Register(exporter Exporter) error {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
name := exporter.Name()
|
name := exporter.Name()
|
||||||
if _, ok := m.registeredExporters[name]; ok {
|
if _, ok := m.registeredExporters[name]; ok {
|
||||||
return fmt.Errorf("failed to register exporter - duplicate name %s", name)
|
return fmt.Errorf("failed to register exporter - duplicate name %s", name)
|
||||||
@@ -114,6 +119,9 @@ func (m *Manager) resolveTargetsFromArgs(exportArgs []string, executionName stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) getExportTarget(export, executionName string) (*Target, error) {
|
func (m *Manager) getExportTarget(export, executionName string) (*Target, error) {
|
||||||
|
m.mu.RLock()
|
||||||
|
defer m.mu.RUnlock()
|
||||||
|
|
||||||
if e, ok := m.registeredExporters[export]; ok {
|
if e, ok := m.registeredExporters[export]; ok {
|
||||||
t := &Target{
|
t := &Target{
|
||||||
exporter: e,
|
exporter: e,
|
||||||
|
|||||||
@@ -132,3 +132,63 @@ func TestDoExport(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestManager_ConcurrentRegistration tests that the Manager can handle concurrent
|
||||||
|
// exporter registration safely. This test is designed to expose race conditions
|
||||||
|
// when run with the -race flag.
|
||||||
|
//
|
||||||
|
// Related issue: #4715
|
||||||
|
func TestManager_ConcurrentRegistration(t *testing.T) {
|
||||||
|
// Create a manager instance
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
// Create multiple test exporters with unique names
|
||||||
|
exporters := []*testExporter{
|
||||||
|
{alias: "", extension: ".csv", name: "csv"},
|
||||||
|
{alias: "", extension: ".json", name: "json"},
|
||||||
|
{alias: "", extension: ".xml", name: "xml"},
|
||||||
|
{alias: "", extension: ".html", name: "html"},
|
||||||
|
{alias: "", extension: ".yaml", name: "yaml"},
|
||||||
|
{alias: "", extension: ".md", name: "markdown"},
|
||||||
|
{alias: "", extension: ".txt", name: "text"},
|
||||||
|
{alias: "", extension: ".log", name: "log"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel to collect errors from goroutines
|
||||||
|
errChan := make(chan error, len(exporters))
|
||||||
|
done := make(chan bool)
|
||||||
|
|
||||||
|
// Register all exporters concurrently
|
||||||
|
for _, exp := range exporters {
|
||||||
|
go func(e *testExporter) {
|
||||||
|
err := m.Register(e)
|
||||||
|
errChan <- err
|
||||||
|
}(exp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect results
|
||||||
|
go func() {
|
||||||
|
for i := 0; i < len(exporters); i++ {
|
||||||
|
err := <-errChan
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to register exporter: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for completion
|
||||||
|
<-done
|
||||||
|
|
||||||
|
// Verify all exporters were registered successfully
|
||||||
|
// Each exporter should be accessible by its name
|
||||||
|
for _, exp := range exporters {
|
||||||
|
target, err := m.getExportTarget(exp.name, "test_exec")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Exporter '%s' was not registered properly: %v", exp.name, err)
|
||||||
|
}
|
||||||
|
if target == nil {
|
||||||
|
t.Errorf("Exporter '%s' returned nil target", exp.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user