# PostgreSQL Service Analysis & Implementation Plan **Date:** 2026-01-27 **Artifacts Analyzed:** `pg.har` (Traffic), `pg.html` (Form Schema) ## 1. Service Overview PostgreSQL "Database as a Service" in Nubes. - **Service Type**: Stateful Application - **Lifecycle Constraint**: **Strict Quarantine (14 days)**. - Like the VM resource, this service **cannot be immediately deleted**. - Attempting to `delete` an active instance returns: `Услуга ПРОМ. Не в состоянии ПРИОСТАНОВЛЕНА более 14 дней`. - **Implication for Terraform**: The `Delete` operation in the provider must likely perform a `Suspend` instead, or Terraform will require the user to manually suspend and wait 14 days before a successful destroy (which is bad UX). - *Strategy Reference*: Similar to `nubes_vm`, `destroy` should trigger `suspend`. ## 2. Resource Parameter Mapping Extracted from `pg.html` (labels) and `pg.har` (values/IDs). | Terraform Argument | Label (UI) | API Key (`instanceOperationCfsParams`) | Type | ID | Notes | | :--- | :--- | :--- | :--- | :--- | :--- | | `nodes_count` | Количество узлов | `resourceInstances` | Int | **80** | | | `cpu` | Квота ядра пода (mili) | `resourceCPU` | Int | **81** | `500` = 0.5 CPU | | `ram` | Квота памяти пода (MB) | `resourceMemory` | Int | **82** | | | `disk_size` | Размер диска для базы данных (GB) | `resourceDisk` | Int | **83** | | | `version` | Версия PostgreSQL | `appVersion` | String | **310** | `"17"`, `"16"`, etc. | | `enable_external_master` | Выделение белого ip для master ноды | `needExternalAddressMaster` | Bool | **145** | | | `ip_space_master` | Имя ipSpace для публикации VIP master | `ipSpaceNameMaster` | String | **102** | Req if external master=true | | `enable_external_slave` | Выделение белого ip для slave ноды | `needExternalAddressSlave` | Bool | **146** | | | `ip_space_slave` | Имя ipSpace для публикации VIP slave | `ipSpaceNameSlave` | String | **---** | Req if external slave=true (ID mismatch in artifacts, verify) | | `backup_schedule` | ext_BACKUP_SCHEDULE | `ext_BACKUP_SCHEDULE` | String | **265** | Cron format | | `backup_retention` | ext_BACKUP_NUM_TO_RETAIN | `ext_BACKUP_NUM_TO_RETAIN` | Int | **266** | | | `config_json` | jsonParameters | `jsonParameters` | JSON | **311** | Postgres runtime config | | `enable_pooler_master` | enablePgPoolerMaster | `enablePgPoolerMaster` | Bool | **---** | ID needs verification | | `enable_pooler_slave` | enablePgPoolerSlave | `enablePgPoolerSlave` | Bool | **---** | ID needs verification | | `allow_no_ssl` | allowNoSSL | `allowNoSSL` | Bool | **---** | ID needs verification | | `dns_name` | (Inferred from HAR) | `dnsRecord` (Likely) | String | **102** | In HAR `k8s-3...` was sent as ID 102, which matches `ipSpaceNameMaster` or DNS? Needs check. | *Note: Some IDs (Pooler, SSL, IP Space Slave) were visible in HTML but their submission IDs need confirmation from a full submission log or deduction.* ### Mismatches/Detailed Logic - **ID 102**: In HAR, `svcOperationCfsParamId: 102` sent value `k8s-3.ext.nubes.ru`. This looks like a DNS name or IP Space name. HTML label is "Имя ipSpace...". - **ID 23**: `instanceOperationUid`? No, HAR shows paramId 23 with UUID value. Check if this is `account_id` or similar. ## 3. Implementation Plan ### Resource Schema (`nubes_postgres`) ```text resource "nubes_postgres" "db" { organization_uuid = "..." vdc_uuid = "..." # If applicable, or just organization context name = "my-pg" version = "17" cpu = 500 ram = 512 disk_gb = 10 nodes = 1 # Network external_master = false # ... # Backup backup_schedule = "0 0 * * *" backup_retention = 7 # Advanced config = jsonencode({ "max_connections" = 100 }) } ``` ### Lifecycle Logic - **Create**: POST `/instanceOperationCfsParams` with mapped IDs. - **Read**: GET `/instances/{id}` -> Parse `cfsParams` from `instanceConfig`. - **Delete**: - Check `explainedStatus`. - If `active`: Call `suspend`. State remains in Terraform (or marked as tainted? No, usually we just update state to show suspended or remove if we consider suspend=deleted). - Given the "quarantine", Terraform `destroy` **cannot** fully remove the resource. - **Decision**: `destroy` will execute `suspend`. The resource will arguably still exist in the cloud. Terraform state should probably be removed to simulate "deletion" from the Infrastructure as Code perspective, alerting the user via logs that actual deletion requires 14 days wait. ## 4. Next Steps 1. Create `internal/provider/postgres_resource.go` (skeleton exists?). 2. Implement `Create` method with correct param IDs. 3. specific testing of the `Suspend` workflow. ## 5. Technical Implementation Details (For AI Agent) ### A. API Request Pattern Analysis of `pg.har` reveals that parameters are **NOT** sent in a single JSON body. The client sends multiple sequential `POST` requests to `/api/v1/index.cfm/instanceOperationCfsParams`. **Payload Schema per Parameter:** ```json { "paramValue": "1", // The value (stringified) "instanceOperationUid": "...", // The operation ID (from the create instance response) "svcOperationCfsParamId": 80 // The specific ID mapped above } ``` **Implementation Strategy:** The provider must loop through the defined parameters and make individual API calls for each one using the `client.CreateInstanceParam` (or similar existing method). ### B. Code References * **Lifecycle Logic**: See `internal/provider/vm_resource.go`. This resource implements the "Suspend instead of Delete" logic required for quarantine-constrained resources. * **API Client**: Check `internal/provider/client_impl.go` for `SendCfsParam` or similar helpers. ### C. Implementation Order (User Request) **PRIORITY 1: Documentation First** The user wants to visualize parameters on the documentation site before code implementation. 1. **Create Documentation File**: * Path: `docs/registry/resources/postgres.md` * Content: Description of the resource, schema table (Inputs determined in Section 2), and an example usage block. 2. **Update Navigation**: * File: `mkdocs.yml` * Action: Add `- Postgres: resources/postgres.md` under `Resources`. 3. **Review**: Ask user to check the visual representation. 4. **Codegen**: Only then proceed to Go implementation. ## 6. Live Instance Analysis (2026-01-27) User provided a full text dump of a running PostgreSQL instance (`test-org-pg-1769262059`). **Key Findings:** 1. **Quarantine Confirmation**: * Operation Log: `delete` failed with "Услуга ПРОМ. Не в состоянии ПРИОСТАНОВЛЕНА более 14 дней". * This confirms `terraform destroy` MUST call `suspend` and remove state, warning the user. 2. **Sensitive Outputs (Credentials)**: * `adminUser`: `postgres` * `adminPass`: (Provided in plain text in UI/API response) * `standbyUser` / `standbyPass`: For HA configurations. 3. **Connection Info**: * `internalConnect.master`: internal K8s DNS (e.g., `postgresqlk8s-master...svc.`). * `externalConnect`: JSON object with `master` and `slave` keys, each containing `ip`, `fqdn`, `uuid`, `isExternal`. 4. **Dependencies**: * Explicit dependency on an S3 Bucket (`s3Uid`: `6d6061cb-...`). * UI shows: "Экземпляр зависит от: S3 Object Storage". * *Implication*: The Terraform resource likely needs an `s3_bucket_id` input argument. 5. **Constraints**: * **Disk Shrink Forbidden**: Operation log shows error "ERROR | Ресурсы под Disk меньше чем в текущем экземпляре". * Terraform validation must prevent reducing `disk_size`. 6. **New Parameters Identified**: * `resourceRealm`: `k8s-3.ext.nubes.ru` (Likely corresponds to ID 102 inferred earlier). * `monitoring`: Contains Grafana URL. ## 7. API Diff Check (2026-02-18) Based on service metadata for Postgres (svcId 90) across prod/test/dev. ### Operations - **prod**: `create`, `delete`, `modify`, `recovery`, `restart`, `resume`, `suspend` - **test/dev**: all prod ops **plus** `create_user`, `delete_user`, `create_database`, `delete_database` ### Input Params (operation-level) - **create**: identical param set across prod/test/dev (21 params) - **modify/delete/restart/resume/suspend**: identical across prod/test/dev - **recovery**: **prod missing** `resourceCPU`, `resourceDisk`, `resourceInstances`, `resourceMemory`, `resourceRealm` compared to test/dev ### Outputs - Output fields are not declared in service metadata. Only visible on instance state (`state_out`, `state_params`). ## 8. YAML Generator Check (service_params_gen) Generator reads service metadata via: - `/index.cfm?endpoint=/services/{svcId}` - `/index.cfm?endpoint=/serviceOperation/{svcOperationId}` Limitations found: - Only collects params for **create** and **modify** operations. - Does **not** include `create_user`, `delete_user`, `create_database`, `delete_database` even when present in API (test/dev). - Outputs are hardcoded defaults, not pulled from API.