fix(accounts): tolerate ambiguous shadow bindings
This commit is contained in:
@@ -375,3 +375,91 @@ func TestSyncProviderAccountsFromImportBatchInfersRouteFromShadowBinding(t *test
|
|||||||
t.Fatalf("provider account view route binding = %+v", view)
|
t.Fatalf("provider account view route binding = %+v", view)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSyncProviderAccountsFromImportBatchLeavesRouteEmptyOnAmbiguousShadowBinding(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
store := openTestDBWithFK(t)
|
||||||
|
ctx := context.Background()
|
||||||
|
hostID := createTestHost(t, store)
|
||||||
|
hostRow, err := store.Hosts().GetByID(ctx, hostID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Hosts().GetByID() error = %v", err)
|
||||||
|
}
|
||||||
|
packID := createTestPack(t, store)
|
||||||
|
providerID, err := store.Providers().Create(ctx, Provider{
|
||||||
|
PackID: packID,
|
||||||
|
ProviderID: "asxs-provider",
|
||||||
|
DisplayName: "ASXS Provider",
|
||||||
|
BaseURL: "https://api.asxs.top/v1",
|
||||||
|
Platform: "openai",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Providers().Create() error = %v", err)
|
||||||
|
}
|
||||||
|
for _, groupID := range []string{"gpt-shared-a", "gpt-shared-b"} {
|
||||||
|
if _, err := store.LogicalGroups().Create(ctx, LogicalGroup{
|
||||||
|
LogicalGroupID: groupID,
|
||||||
|
DisplayName: groupID,
|
||||||
|
Status: "active",
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("LogicalGroups().Create(%s) error = %v", groupID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, routeID := range []string{"route-a", "route-b"} {
|
||||||
|
logicalGroupID := "gpt-shared-a"
|
||||||
|
if routeID == "route-b" {
|
||||||
|
logicalGroupID = "gpt-shared-b"
|
||||||
|
}
|
||||||
|
if _, err := store.LogicalGroupRoutes().Create(ctx, LogicalGroupRoute{
|
||||||
|
RouteID: routeID,
|
||||||
|
LogicalGroupID: logicalGroupID,
|
||||||
|
Name: routeID,
|
||||||
|
Status: "active",
|
||||||
|
Priority: 10,
|
||||||
|
Weight: 100,
|
||||||
|
ShadowGroupID: "group-1",
|
||||||
|
ShadowHostID: hostRow.HostID,
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("LogicalGroupRoutes().Create(%s) error = %v", routeID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
batchID, err := store.ImportBatches().Create(ctx, ImportBatch{
|
||||||
|
HostID: hostID,
|
||||||
|
PackID: packID,
|
||||||
|
ProviderID: providerID,
|
||||||
|
Mode: "strict",
|
||||||
|
BatchStatus: "succeeded",
|
||||||
|
AccessStatus: "subscription_ready",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ImportBatches().Create() error = %v", err)
|
||||||
|
}
|
||||||
|
if _, err := store.ImportBatchItems().Create(ctx, ImportBatchItem{
|
||||||
|
BatchID: batchID,
|
||||||
|
KeyFingerprint: "sha256:key1",
|
||||||
|
AccountStatus: "passed",
|
||||||
|
ProbeSummaryJSON: `{"account_id":"account-1","probe_status":"passed"}`,
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("ImportBatchItems().Create() error = %v", err)
|
||||||
|
}
|
||||||
|
for _, resource := range []ManagedResource{
|
||||||
|
{BatchID: batchID, HostID: hostID, ResourceType: "group", HostResourceID: "group-1", ResourceName: "ASXS Group"},
|
||||||
|
{BatchID: batchID, HostID: hostID, ResourceType: "account", HostResourceID: "account-1", ResourceName: "asxs-01"},
|
||||||
|
} {
|
||||||
|
if _, err := store.ManagedResources().Create(ctx, resource); err != nil {
|
||||||
|
t.Fatalf("ManagedResources().Create(%s) error = %v", resource.ResourceType, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := SyncProviderAccountsFromImportBatch(ctx, store, batchID); err != nil {
|
||||||
|
t.Fatalf("SyncProviderAccountsFromImportBatch() error = %v", err)
|
||||||
|
}
|
||||||
|
account, err := store.ProviderAccounts().GetByHostIDAndAccountID(ctx, hostID, "account-1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ProviderAccounts().GetByHostIDAndAccountID() error = %v", err)
|
||||||
|
}
|
||||||
|
if account.RouteID != "" {
|
||||||
|
t.Fatalf("provider account route binding = %+v, want empty route on ambiguous shadow binding", account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -134,6 +134,9 @@ func resolveProviderAccountRouteBinding(ctx context.Context, store *DB, shadowHo
|
|||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return LogicalGroupRoute{}, err
|
return LogicalGroupRoute{}, err
|
||||||
}
|
}
|
||||||
|
if isAmbiguousProviderAccountRouteBinding(err) {
|
||||||
|
return LogicalGroupRoute{}, sql.ErrNoRows
|
||||||
|
}
|
||||||
return LogicalGroupRoute{}, fmt.Errorf("resolve logical route for provider account shadow binding %q/%q: %w", shadowHostID, shadowGroupID, err)
|
return LogicalGroupRoute{}, fmt.Errorf("resolve logical route for provider account shadow binding %q/%q: %w", shadowHostID, shadowGroupID, err)
|
||||||
}
|
}
|
||||||
return route, nil
|
return route, nil
|
||||||
@@ -209,3 +212,10 @@ func preserveManagedProviderAccountStatus(row *ProviderAccount, existing Provide
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isAmbiguousProviderAccountRouteBinding(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return strings.Contains(err.Error(), "multiple logical group routes match shadow binding")
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user