@@ -4,10 +4,6 @@ import (
44 "context"
55 "fmt"
66 "strings"
7- "time"
8-
9- "github.com/aws/aws-sdk-go-v2/aws/arn"
10- "github.com/pkg/errors"
117
128 "github.com/block/Version-Guard/pkg/registry"
139 "github.com/block/Version-Guard/pkg/types"
@@ -32,7 +28,6 @@ func NewOpenSearchInventorySource(client *Client, reportID string) *OpenSearchIn
3228}
3329
3430// WithRegistryClient adds optional registry integration for service attribution.
35- // When tags are missing, the registry will be queried to map AWS account → service.
3631func (s * OpenSearchInventorySource ) WithRegistryClient (registryClient registry.Client ) * OpenSearchInventorySource {
3732 s .registryClient = registryClient
3833 return s
@@ -57,58 +52,37 @@ func (s *OpenSearchInventorySource) CloudProvider() types.CloudProvider {
5752 return types .CloudProviderAWS
5853}
5954
55+ var openSearchRequiredColumns = []string {
56+ colHeaderExternalID ,
57+ colHeaderName ,
58+ colHeaderNativeType ,
59+ colHeaderAccountID ,
60+ colHeaderVersion ,
61+ colHeaderRegion ,
62+ colHeaderTags ,
63+ colHeaderEngineKind ,
64+ }
65+
6066// ListResources fetches all OpenSearch resources from Wiz
61- //
62- //nolint:dupl // follows established inventory source pattern (aurora, elasticache)
6367func (s * OpenSearchInventorySource ) ListResources (ctx context.Context , resourceType types.ResourceType ) ([]* types.Resource , error ) {
6468 if resourceType != types .ResourceTypeOpenSearch {
6569 return nil , fmt .Errorf ("unsupported resource type: %s (only OPENSEARCH supported)" , resourceType )
6670 }
6771
68- // Fetch report data
69- rows , err := s .client .GetReportData (ctx , s .reportID )
70- if err != nil {
71- return nil , errors .Wrap (err , "failed to fetch Wiz report data" )
72- }
73-
74- if len (rows ) < 2 {
75- // Empty report (only header row)
76- return []* types.Resource {}, nil
77- }
78-
79- // Skip header row, parse data rows
80- var resources []* types.Resource
81- for i , row := range rows [1 :] {
82- if len (row ) < colMinRequired {
83- // Skip malformed rows
84- continue
85- }
86-
87- // Filter for OpenSearch domain resources only
88- nativeType := row [colNativeType ]
89- if ! isOpenSearchResource (nativeType ) {
90- continue
91- }
92-
93- resource , err := s .parseOpenSearchRow (ctx , row )
94- if err != nil {
95- // Log error but continue processing other rows
96- // TODO: add proper logging
97- _ = fmt .Sprintf ("row %d: failed to parse OpenSearch resource: %v" , i + 1 , err )
98- continue
99- }
100-
101- if resource != nil {
102- resources = append (resources , resource )
103- }
104- }
105-
106- return resources , nil
72+ return parseWizReport (
73+ ctx ,
74+ s .client ,
75+ s .reportID ,
76+ openSearchRequiredColumns ,
77+ func (cols columnIndex , row []string ) bool {
78+ return isOpenSearchResource (cols .col (row , colHeaderNativeType ))
79+ },
80+ s .parseOpenSearchRow ,
81+ )
10782}
10883
10984// GetResource fetches a specific OpenSearch resource by ARN
11085func (s * OpenSearchInventorySource ) GetResource (ctx context.Context , resourceType types.ResourceType , id string ) (* types.Resource , error ) {
111- // For Wiz source, we fetch all and filter
11286 resources , err := s .ListResources (ctx , resourceType )
11387 if err != nil {
11488 return nil , err
@@ -123,73 +97,15 @@ func (s *OpenSearchInventorySource) GetResource(ctx context.Context, resourceTyp
12397 return nil , fmt .Errorf ("resource not found: %s" , id )
12498}
12599
126- // parseOpenSearchRow parses a single CSV row into a Resource
127- func (s * OpenSearchInventorySource ) parseOpenSearchRow (ctx context.Context , row []string ) (* types.Resource , error ) {
128- resourceARN := row [colARN ]
129- if resourceARN == "" {
130- return nil , fmt .Errorf ("missing ARN" )
131- }
132-
133- // Parse ARN
134- parsedARN , err := arn .Parse (resourceARN )
100+ // parseOpenSearchRow parses a single CSV row into a Resource.
101+ // Uses the shared parseAWSResourceRow helper, then normalizes the version
102+ // to strip the "OpenSearch_" prefix that Wiz sometimes includes.
103+ func (s * OpenSearchInventorySource ) parseOpenSearchRow (ctx context.Context , cols columnIndex , row []string ) (* types.Resource , error ) {
104+ resource , err := parseAWSResourceRow (ctx , cols , row , types .ResourceTypeOpenSearch , normalizeOpenSearchKind , s .tagConfig , s .registryClient )
135105 if err != nil {
136- return nil , errors .Wrapf (err , "invalid ARN: %s" , resourceARN )
137- }
138-
139- // Extract metadata
140- resourceName := row [colResourceName ]
141- accountID := row [colAWSAccountID ]
142- if accountID == "" {
143- accountID = parsedARN .AccountID
144- }
145-
146- engine := normalizeOpenSearchKind (row [colEngineKind ])
147- version := normalizeOpenSearchVersion (row [colEngineVersion ])
148- region := row [colRegion ]
149-
150- // Parse tags
151- tagsJSON := row [colTags ]
152- tags , err := ParseTags (tagsJSON )
153- if err != nil {
154- // Non-fatal, just use empty tags
155- tags = make (map [string ]string )
156- }
157-
158- // Extract service name from tags (using configurable tag keys)
159- service := s .tagConfig .GetAppTag (tags )
160- if service == "" {
161- // Try registry lookup by AWS account (if registry is configured)
162- if s .registryClient != nil {
163- if serviceInfo , err := s .registryClient .GetServiceByAWSAccount (ctx , accountID , region ); err == nil {
164- service = serviceInfo .ServiceName
165- }
166- // Ignore registry errors - fall through to name parsing
167- }
168-
169- // Final fallback: extract from resource name or ARN
170- if service == "" {
171- service = extractServiceFromName (resourceName )
172- }
173- }
174-
175- // Extract brand (using configurable tag keys)
176- brand := s .tagConfig .GetBrandTag (tags )
177-
178- resource := & types.Resource {
179- ID : resourceARN ,
180- Name : resourceName ,
181- Type : types .ResourceTypeOpenSearch ,
182- CloudProvider : types .CloudProviderAWS ,
183- Service : service ,
184- CloudAccountID : accountID ,
185- CloudRegion : region ,
186- Brand : brand ,
187- CurrentVersion : version ,
188- Engine : engine ,
189- Tags : tags ,
190- DiscoveredAt : time .Now (),
106+ return nil , err
191107 }
192-
108+ resource . CurrentVersion = normalizeOpenSearchVersion ( resource . CurrentVersion )
193109 return resource , nil
194110}
195111
0 commit comments