From a5df6dca6240b97960dabf80cf5be743218f39f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Sun, 5 Jul 2026 08:17:56 +0400 Subject: [PATCH] =?UTF-8?q?ARCHITECTURE:=20PRIMARY=20source=20of=20truth?= =?UTF-8?q?=20=E2=80=94=20action=20rules=20(redeploy=20inline,=20others=20?= =?UTF-8?q?excluded),=20401=20retry,=20API=20endpoint=20auto-detect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devops/ARCHITECTURE.md | 62 +++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/devops/ARCHITECTURE.md b/devops/ARCHITECTURE.md index 3bf1284..33b8964 100644 --- a/devops/ARCHITECTURE.md +++ b/devops/ARCHITECTURE.md @@ -1,8 +1,10 @@ -# Provider Architecture (Source of Truth) +# Provider Architecture (PRIMARY SOURCE OF TRUTH) + +**⛔ THIS FILE IS THE FOUNDATION. ALL CODE AND SCRIPTS ARE DERIVED FROM IT.** This document defines the project-wide architecture and rules for generation, -provider behavior, and documentation. It is the single source of truth for -implementation and DevOps workflow. +provider behavior, and documentation. Any change to provider logic MUST be +reflected here FIRST, then implemented in `gen_v2` and other tools. ## Core Principles @@ -12,6 +14,7 @@ implementation and DevOps workflow. 4) Documentation is generated from the same YAML. 5) Build artifacts for 3 OS targets are published to the registry, and docs are published to the website. +6) `devops/ARCHITECTURE.md` (this file) is the primary spec. Code follows. ## Service Selection @@ -19,6 +22,12 @@ implementation and DevOps workflow. - Each line starts with service_id, followed by service name/alias. - Operation timeouts source is defined by: `devops/config/operation_timeouts.json`. +## API Endpoint + +The provider supports two API styles, auto-detected by `NUBES_API_ENDPOINT`: +- **Legacy proxy**: contains `index.cfm` → `?endpoint=/path` +- **REST Gateway**: no `index.cfm` → direct path concatenation + ## Unified YAML (Per Service) One YAML file per service. This is the only input for: @@ -39,16 +48,33 @@ Required top-level fields: ### Operations: Kinds and Rules Each operation has a kind: -- kind: instance (CRUD for the service instance, plus suspend/resume) -- kind: subresource (CRUD for objects inside the service, e.g. users/databases) -- kind: action (one-shot operations, e.g. restart/recovery/redeploy) + +**instance** — CRUD for the service instance, plus suspend/resume: +| API action | Terraform behavior | +|---|---| +| create | `terraform apply` (new resource) | +| delete | `terraform destroy` | +| modify | `terraform apply` (params changed) | +| suspend | `terraform destroy` when `suspend_on_destroy=true` | +| resume | `terraform apply` when `adopt_existing_on_create=true` | + +**subresource** — CRUD for objects inside the service (users, databases, topics): +- Exposed as separate resources: `nubes_{service}_{subresource}` +- Identity = `{parent_instance_uid, subresource_key}` (e.g. `{postgres_id, username}`) +- Supports `adopt_existing_on_create` — if subresource already exists, adopt it instead of failing + +**action** — one-shot operations. **Only `redeploy` is included**: +- `redeploy` → **inline**: field `git_revision` in the main resource. When it changes, call redeploy instead of (or after) modify +- `restart`, `recovery`, `reconcile` → **excluded**. These are manual operational tasks, performed via UI only. Reason: + - `restart` — modify handles pod restart when needed + - `recovery` — creates a new instance from backup, not a modification of existing + - `reconcile` — sync after manual changes; Terraform owns its own state ### Idempotency Rules - instance CRUD is idempotent via standard Terraform behavior. - subresource CRUD is idempotent by resource identity. -- action operations are idempotent via a trigger field (e.g. run_id/nonce). - If the trigger does not change, the action is not re-run. +- `redeploy`: idempotent via `git_revision` field — if unchanged, no redeploy ### Lifecycle Behavior @@ -83,6 +109,13 @@ Each operation has a kind: - Core is universal: no service-specific logic inside the core. - Generated service resources contain only schema/params and references. +### API Resilience + +- Core MUST retry transient 401 errors from Gateway (3 attempts, exponential backoff). + Gateway may temporarily reject valid JWT tokens. +- GET operations (GetInstanceState, GetInstanceStateRaw) retry 401 with 2s/4s/8s backoff. +- `doRequest` treats 401 as retryable for GET requests (alongside 429, 502, 503, 504). + ### Subresource Resources Subresource operations are exposed as standard resources. @@ -98,14 +131,17 @@ resource "nubes_postgres_user" "user1" { role = "app_user" } -### Action Resources +### Redeploy (inline action) + +Services with `redeploy` operation get a `git_revision` field in the main resource. +Changing `git_revision` triggers `redeploy` instead of `modify`. -Action operations are exposed as action resources with a trigger field. Example HCL: -resource "nubes_postgres_restart" "restart1" { - postgres_id = nubes_postgres.db.id - run_id = "2026-02-20-001" +resource "nubes_flask" "app" { + resource_name = "my-flask" + git_revision = "abc123" # ← change this to trigger redeploy + app_configuration = jsonencode({...}) } ## Concurrency and State Locking