diff --git a/acceptance/bundle/resources/vector_search_endpoints/drift/recreated_same_name/output.txt b/acceptance/bundle/resources/vector_search_endpoints/drift/recreated_same_name/output.txt index 08afd3157e..d24c6bea8d 100644 --- a/acceptance/bundle/resources/vector_search_endpoints/drift/recreated_same_name/output.txt +++ b/acceptance/bundle/resources/vector_search_endpoints/drift/recreated_same_name/output.txt @@ -38,9 +38,9 @@ Remote recreated endpoint UUID: [REMOTE_RECREATED_ENDPOINT_UUID] === Plan detects the UUID change and proposes recreate >>> [CLI] bundle plan recreate vector_search_endpoints.my_endpoint -update vector_search_endpoints.my_endpoint.permissions +create vector_search_endpoints.my_endpoint.permissions -Plan: 1 to add, 1 to change, 1 to delete, 0 unchanged +Plan: 2 to add, 0 to change, 1 to delete, 0 unchanged === Deploy recreates the endpoint and rebinds permissions to the new UUID >>> [CLI] bundle deploy diff --git a/acceptance/bundle/resources/vector_search_endpoints/drift/recreated_same_name/script b/acceptance/bundle/resources/vector_search_endpoints/drift/recreated_same_name/script index 0a17aa3152..8f28189d7f 100644 --- a/acceptance/bundle/resources/vector_search_endpoints/drift/recreated_same_name/script +++ b/acceptance/bundle/resources/vector_search_endpoints/drift/recreated_same_name/script @@ -33,7 +33,7 @@ if [ "$original_endpoint_uuid" = "$remote_recreated_endpoint_uuid" ]; then fi title "Plan detects the UUID change and proposes recreate" -trace $CLI bundle plan | contains.py "recreate vector_search_endpoints.my_endpoint" "update vector_search_endpoints.my_endpoint.permissions" +trace $CLI bundle plan | contains.py "recreate vector_search_endpoints.my_endpoint" "create vector_search_endpoints.my_endpoint.permissions" title "Deploy recreates the endpoint and rebinds permissions to the new UUID" trace $CLI bundle deploy diff --git a/libs/testserver/permissions.go b/libs/testserver/permissions.go index 3589b9d704..e7983b1afa 100644 --- a/libs/testserver/permissions.go +++ b/libs/testserver/permissions.go @@ -107,6 +107,19 @@ func (s *FakeWorkspace) GetPermissions(req Request) any { } responseObjectID := fmt.Sprintf("/%s/%s", requestObjectType, objectId) + + // V2 permissions APIs cascade-delete ACLs with the parent, so the cloud + // returns 404 once the parent is gone. V1 APIs (jobs, pipelines, etc.) + // retain ACL data after delete via async/soft delete; for those, we + // fall through to the "empty ACL on miss" branch below, which is close + // enough. New V2 resources should add a case to permissionsParentExists. + if !s.permissionsParentExists(requestObjectType, objectId) { + return Response{ + StatusCode: 404, + Body: map[string]string{"message": fmt.Sprintf("%s %s not found.", requestObjectType, objectId)}, + } + } + permissions, exists := s.Permissions[responseObjectID] if !exists { @@ -123,6 +136,23 @@ func (s *FakeWorkspace) GetPermissions(req Request) any { } } +// permissionsParentExists reports whether the parent object backing a +// permissions request exists in workspace state. Returns true for resource +// types without a parent-existence check wired up; V1 resources rely on +// that fallback to keep their "empty ACL on miss" behavior. +func (s *FakeWorkspace) permissionsParentExists(requestObjectType, objectId string) bool { + switch requestObjectType { + case "vector-search-endpoints": + for _, ep := range s.VectorSearchEndpoints { + if ep.Id == objectId { + return true + } + } + return false + } + return true +} + func (s *FakeWorkspace) SetPermissions(req Request) any { defer s.LockUnlock()()