Skip to content

baidu resource enrich#111

Open
Center-Sun wants to merge 2 commits into
antgroup:mainfrom
Center-Sun:feat/baidu-resource-enrich
Open

baidu resource enrich#111
Center-Sun wants to merge 2 commits into
antgroup:mainfrom
Center-Sun:feat/baidu-resource-enrich

Conversation

@Center-Sun
Copy link
Copy Markdown
Contributor

Thank you for your contribution to CloudRec!

What About:

  • Server (java)
  • Collector (go)
  • Rule (opa)

Description:

Two related commits to expand baidu cloud coverage:

1. feat(collector): add baidu ENI and ACL resources (d8c626d)

Adds two previously-uncollected resource types under BAIDU_CLOUD:

  • ENIbcc.Client.ListEnis() across all baidu BCC regions
  • ACLbcc.Client.ListAclEntrys() keyed by VPC

Both follow the flat-instance convention (struct embedding *sdk.Foo + sibling fields), so paths read naturally as $.eniId, $.vpcId, etc.

2. feat(collector): enrich baidu BLB and APPBLB with SDK-omitted fields (8371686)

The baidu Go SDK's BLBModel / AppBLBModel / AppAllListenerModel / AppServerGroupPort structs under-model the actual API responses. Fields the SDK silently drops
are now captured:

API New fields preserved
GET /v1/blb, GET /v1/appblb type, underlayVip, expireTime, billingMethod, paymentTiming, performanceLevel, allowModify,
modificationProtectionReason
GET /v1/blb/{id}/listener backendPortType, healthCheckValid
GET /v1/appblb/{id}/listener description
GET /v1/appblb/{id}/appservergroup portList[].healthCheckValid

Each affected Detail field switches to a struct that embeds the SDK type and appends the missing fields, so existing JSONPaths ($.Blb.blbId, $.AppBLB.name,
$.AppServerGroupList[].portList[].port, etc.) are preserved bit-for-bit. New fields appear as parallel siblings under the same wrapper.

The SDK's typed DescribeLoadBalancers / DescribeAllListeners / DescribeAppServerGroup calls are replaced with direct bce.RequestBuilder calls bound to the same
URI and pagination contract, so the request count is unchanged — a single API call now feeds both the SDK-modeled and new fields.

Validation

End-to-end run against two production-style baidu cloud accounts:

  • 5 BLB rows + 609 APPBLB rows upserted into cloud_resource_instance_v1
  • Every fresh $.Blb.* and $.AppBLB.* instance has exactly 23 keys = 15 SDK-modeled + 8 newly preserved
  • description populated on 608/608 APPBLBs with non-empty listeners (1 CCE-managed BLB with listenerList=[] returns null — same behavior as pre-change SDK call)
  • portList[].healthCheckValid populated on 15/15 APPBLBs with non-empty portList
  • All pre-existing field paths under $.Blb.* / $.AppBLB.* resolve unchanged

Adds two new resource types to the baidu collector:

- ENI: region-scoped collector that enumerates VPCs in the region and
  then calls ListEni per vpcId (the baidu SDK requires a vpcId on the
  list call). Captures unattached ENIs and ENIs attached to non-BCC
  resources (CCE workers, etc.). The Detail is a flat alias of eni.Eni
  so the persisted instance JSON keeps the API's top-level field names
  ($.eniId, $.name, $.privateIpSet, ...), matching the JSONPath shape
  downstream consumers already expect.

- ACL: region-scoped collector that enumerates VPCs and emits one ACL
  record per VPC carrying the full set of subnet ACL entries returned
  by ListAclEntrys. The Detail embeds *vpc.ListAclEntrysResult to keep
  its fields (vpcId, vpcName, vpcCidr, aclEntrys) at the top level,
  with sibling resourceId/resourceName fields holding the
  "acl-{vpcId}" prefix used as the canonical resource identifier.

Registered in platform_config and wired into InitServices so both
resource types pick up clients from the existing baidu Services bag.
The baidu SDK's BLBModel / AppBLBModel / AppAllListenerModel /
AppServerGroupPort types under-model the actual /v1/blb, /v1/appblb,
/v1/{blb,appblb}/{id}/listener and /v1/appblb/{id}/appservergroup API
responses — fields like type, billingMethod, paymentTiming,
performanceLevel, allowModify, modificationProtectionReason,
underlayVip, expireTime on the load balancer; description on the
listener; healthCheckValid on AppServerGroupPort and on BLB listener
are silently dropped by the SDK structs.

Each affected Detail field switches to an enriched struct that embeds
the SDK type and appends the missing fields. Existing JSONPaths
($.Blb.blbId, $.AppBLB.name, $.AppServerGroupList[].portList[].port,
etc.) are preserved bit-for-bit; new fields appear as parallel siblings
under the same wrapper. The SDK-typed DescribeLoadBalancers /
DescribeAllListeners / DescribeAppServerGroup calls are replaced with
direct bce.RequestBuilder calls bound to the same URI and pagination
contract so a single API call still feeds both the existing and new
fields.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for collecting ENI and ACL resources from Baidu Cloud and enhances the BLB and AppBLB collectors by introducing 'enriched' structs and custom request builders to capture API fields missing from the standard SDK. The review feedback identifies a high-severity issue with an incorrect JSONPath for ENI addresses, suggests refactoring duplicated client initialization logic in services.go, and points out several instances in the BLB collectors where API errors are swallowed, which could result in incomplete resource data.

RowField: schema.RowField{
ResourceId: "$.eniId",
ResourceName: "$.name",
Address: "$.privateIpSet[0].publicIpAddress",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The JSONPath $.privateIpSet[0].publicIpAddress is likely incorrect. The eni.Eni struct in the Baidu SDK contains privateIpAddress within the privateIpSet elements, but does not include a publicIpAddress field. Since the collector uses the SDK struct, this field will be missing in the serialized JSON, and the JSONPath will fail to resolve. If the intention is to capture the primary private IP, please use $.privateIpSet[0].privateIpAddress.

Suggested change
Address: "$.privateIpSet[0].publicIpAddress",
Address: "$.privateIpSet[0].privateIpAddress",

Comment on lines +105 to +121
case VPC, ACL:
vpcClient, err := vpc.NewClient(param.AK, param.SK, param.Region)
if err != nil {
log.GetWLogger().Warn(fmt.Sprintf("init vpc client failed, err: %s", err))
}
s.VPCClient = vpcClient
case ENI:
vpcClient, err := vpc.NewClient(param.AK, param.SK, param.Region)
if err != nil {
log.GetWLogger().Warn(fmt.Sprintf("init vpc client failed, err: %s", err))
}
s.VPCClient = vpcClient
eniClient, err := eni.NewClient(param.AK, param.SK, param.Region)
if err != nil {
log.GetWLogger().Warn(fmt.Sprintf("init eni client failed, err: %s", err))
}
s.ENIClient = eniClient
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The initialization logic for VPCClient is duplicated across the VPC, ACL, and ENI resource types. This can be refactored to reduce redundancy and improve maintainability.

Suggested change
case VPC, ACL:
vpcClient, err := vpc.NewClient(param.AK, param.SK, param.Region)
if err != nil {
log.GetWLogger().Warn(fmt.Sprintf("init vpc client failed, err: %s", err))
}
s.VPCClient = vpcClient
case ENI:
vpcClient, err := vpc.NewClient(param.AK, param.SK, param.Region)
if err != nil {
log.GetWLogger().Warn(fmt.Sprintf("init vpc client failed, err: %s", err))
}
s.VPCClient = vpcClient
eniClient, err := eni.NewClient(param.AK, param.SK, param.Region)
if err != nil {
log.GetWLogger().Warn(fmt.Sprintf("init eni client failed, err: %s", err))
}
s.ENIClient = eniClient
case VPC, ACL, ENI:
vpcClient, err := vpc.NewClient(param.AK, param.SK, param.Region)
if err != nil {
log.GetWLogger().Warn(fmt.Sprintf("init vpc client failed, err: %s", err))
}
s.VPCClient = vpcClient
if cloudAccountParam.ResourceType == ENI {
eniClient, err := eni.NewClient(param.AK, param.SK, param.Region)
if err != nil {
log.GetWLogger().Warn(fmt.Sprintf("init eni client failed, err: %s", err))
}
s.ENIClient = eniClient
}

Comment on lines +162 to 185
func describeAppAllListeners(ctx context.Context, client *appblb.Client, blbId string) (listenerList []enrichedAppListener) {
marker := ""
for {
response, err := client.DescribeAppAllListeners(blbId, args)
if err != nil {
result := &enrichedDescribeAppAllListenersResult{}
rb := bce.NewRequestBuilder(client).
WithMethod("GET").
WithURL("/v1/appblb/" + blbId + "/listener").
WithQueryParam("maxKeys", strconv.Itoa(50)).
WithResult(result)
if marker != "" {
rb = rb.WithQueryParam("marker", marker)
}
if err := rb.Do(); err != nil {
log.CtxLogger(ctx).Warn("DescribeAppAllListeners error", zap.Error(err))
return
}
listenerList = append(listenerList, response.ListenerList...)
if response.NextMarker == "" {
listenerList = append(listenerList, result.ListenerList...)
if result.NextMarker == "" {
break
}
args.Marker = response.NextMarker
marker = result.NextMarker
}

return listenerList
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The function describeAppAllListeners (and similarly describeAppServerGroup at line 207) swallows errors from the API call, returning a potentially empty or partial list without notifying the caller. This can lead to incomplete resource data being collected silently. Consider updating these functions to return an error along with the results, allowing the caller to handle API failures more robustly.

Comment on lines +144 to 167
func describeAllListeners(ctx context.Context, client *blb.Client, blbId string) (listenerList []enrichedBLBListener) {
marker := ""
for {
response, err := client.DescribeAllListeners(blbId, args)
if err != nil {
result := &enrichedDescribeAllListenersResult{}
rb := bce.NewRequestBuilder(client).
WithMethod("GET").
WithURL("/v1/blb/" + blbId + "/listener").
WithQueryParam("maxKeys", strconv.Itoa(50)).
WithResult(result)
if marker != "" {
rb = rb.WithQueryParam("marker", marker)
}
if err := rb.Do(); err != nil {
log.CtxLogger(ctx).Warn("DescribeAllListeners error", zap.Error(err))
return
}
listenerList = append(listenerList, response.AllListenerList...)
if response.NextMarker == "" {
listenerList = append(listenerList, result.ListenerList...)
if result.NextMarker == "" {
break
}
args.Marker = response.NextMarker
marker = result.NextMarker
}

return listenerList
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The function describeAllListeners swallows errors from the rb.Do() call. If the API request fails, it returns an empty or partial list, which might result in incomplete data collection. It is recommended to return the error to the caller for better error handling and visibility.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant