Pulumi Go SDK for GCP 销毁SQL Server资源失败报错排查
问题场景
使用Pulumi Go SDK编排GCP资源时,执行栈销毁操作,尝试删除已部署的Cloud SQL DatabaseInstance、Database、访问密码、数据库用户资源过程中触发报错,核心错误日志如下:
21:00:33 [2022-07-05T18:00:33.872Z] Diagnostics: 21:00:33 [2022-07-05T18:00:33.874Z] gcp:sql:User (gcp-test02-user:myuser): 21:00:33 [2022-07-05T18:00:33.875Z] error: deleting urn:pulumi:us-east4-gcp-test02::cluster::gcp:myuser/sql:Database$gcp:sql/user:User::gcp-test02-user:myuser: 1 error occurred: 21:00:33 [2022-07-05T18:00:33.876Z] * Error, failed to deleteuser myuser in instance gcp-test02-1b95d9a: googleapi: Error 400: Invalid request: failed to delete user myuser: . role "myuser" cannot be dropped because some objects depend on it Details: 640 objects in database mydatabases., invalid 21:00:33 [2022-07-05T18:00:33.877Z] 21:00:33 [2022-07-05T18:00:33.877Z] gcp:sql:Database (gcp-test02-db:mydatabases): 21:00:33 [2022-07-05T18:00:33.879Z] error: deleting urn:pulumi:us-east4-auto-mgmt-console-gcp-test02::cluster::gcp:myuser/sql:Database$gcp:sql/database:Database::gcp-test02-db:mydatabases: 1 error occurred: 21:00:33 [2022-07-05T18:00:33.880Z] * Error when reading or editing Database: googleapi: Error 400: Invalid request: failed to delete database "sentinellabs". Detail: pq: database "sentinellabs" is being accessed by other users. (Please use psql client to delete database that is not owned by "cloudsqlsuperuser")., invalid 21:00:33 [2022-07-05T18:00:33.881Z] 21:00:33 [2022-07-05T18:00:33.881Z] pulumi:pulumi:Stack (cluster-us-east4-auto-mgmt-console-gcp-test02): 21:00:33 [2022-07-05T18:00:33.882Z] error: update failed
根因分析
报错由三个层面的问题共同触发:
- 数据库用户删除校验拦截:本次使用的Cloud SQL为PostgreSQL引擎,待删除用户
myuser在mydatabases库下持有640个归属对象(表、视图、函数、权限绑定等),PostgreSQL的角色强校验规则不允许直接删除仍持有对象依赖的数据库用户。 - 数据库删除校验拦截:一是报错涉及的
mydatabases、sentinellabs库存在活跃客户端连接,数据库处于被访问状态无法删除;二是sentinellabs库的所有者不是GCP Cloud SQL默认的超管角色cloudsqlsuperuser,GCP管控平面的数据库删除接口无权操作该类库,明确要求通过psql客户端执行删除。 - 销毁顺序不匹配:Pulumi默认并行发起用户、数据库资源的删除请求,没有先清理数据库内部对象依赖、断开活跃连接,直接触发数据库层的校验规则,导致资源删除流程中断,整个栈销毁失败。
修复操作步骤
- 连接数据库实例完成前置清理
通过Cloud SQL代理、VPC内网连接或公网连接方式,使用PostgreSQL管理员账号(默认用户为postgres)通过psql登录目标Cloud SQL实例,对所有报错涉及的数据库执行以下操作:- 强制断开目标库的所有非当前会话连接,替换命令中的库名为实际待删除的库名,依次处理
mydatabases、sentinellabs:SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '替换为目标库名' AND pid <> pg_backend_pid(); - 清理待删除用户
myuser的所有依赖:将该用户持有的所有对象所有权转移给超管角色,清理残留权限配置:REASSIGN OWNED BY myuser TO cloudsqlsuperuser; DROP OWNED BY myuser;
- 强制断开目标库的所有非当前会话连接,替换命令中的库名为实际待删除的库名,依次处理
- 手动删除阻塞的数据库和用户资源
在psql会话中依次执行命令,删除非超管所有的数据库和待清理用户:DROP DATABASE IF EXISTS mydatabases; DROP DATABASE IF EXISTS sentinellabs; DROP ROLE IF EXISTS myuser; - 同步Pulumi状态完成剩余资源销毁
- 回到Pulumi操作目录,执行
pulumi refresh,拉取云端最新资源状态,让Pulumi状态文件感知到上述数据库、用户资源已经被手动移除,自动从栈资源清单中剔除对应条目。 - 重新执行
pulumi destroy,此时剩余的Cloud SQL实例、访问密码等资源已无依赖阻塞,可正常完成销毁。
- 回到Pulumi操作目录,执行
长期规避方案
- 在Pulumi代码中显式配置资源依赖关系,指定SQL User、SQL Database资源必须在所有会操作数据库内部对象的业务资源(比如初始化表结构、写入业务数据的自定义资源)销毁完成后再执行删除,避免销毁顺序错乱触发校验拦截。
- 若需要通过Pulumi全生命周期管理数据库内部的schema、表、权限等对象,可搭配PostgreSQL Provider在代码中显式定义这些内部对象的生命周期,确保销毁时按顺序先清理内部对象,再删除用户、数据库和实例资源。
内容的提问来源于stack exchange,提问作者Chanan Berler
相关产品推荐
相关产品推荐

