ARCHITECTURE: PRIMARY source of truth — action rules (redeploy inline, others excluded), 401 retry, API endpoint auto-detect

This commit is contained in:
“Naeel”
2026-07-05 08:17:56 +04:00
parent e9901c6309
commit a5df6dca62
+49 -13
View File
@@ -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, This document defines the project-wide architecture and rules for generation,
provider behavior, and documentation. It is the single source of truth for provider behavior, and documentation. Any change to provider logic MUST be
implementation and DevOps workflow. reflected here FIRST, then implemented in `gen_v2` and other tools.
## Core Principles ## Core Principles
@@ -12,6 +14,7 @@ implementation and DevOps workflow.
4) Documentation is generated from the same YAML. 4) Documentation is generated from the same YAML.
5) Build artifacts for 3 OS targets are published to the registry, and docs are 5) Build artifacts for 3 OS targets are published to the registry, and docs are
published to the website. published to the website.
6) `devops/ARCHITECTURE.md` (this file) is the primary spec. Code follows.
## Service Selection ## Service Selection
@@ -19,6 +22,12 @@ implementation and DevOps workflow.
- Each line starts with service_id, followed by service name/alias. - Each line starts with service_id, followed by service name/alias.
- Operation timeouts source is defined by: `devops/config/operation_timeouts.json`. - 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) ## Unified YAML (Per Service)
One YAML file per service. This is the only input for: One YAML file per service. This is the only input for:
@@ -39,16 +48,33 @@ Required top-level fields:
### Operations: Kinds and Rules ### Operations: Kinds and Rules
Each operation has a kind: 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) **instance** — CRUD for the service instance, plus suspend/resume:
- kind: action (one-shot operations, e.g. restart/recovery/redeploy) | 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 ### Idempotency Rules
- instance CRUD is idempotent via standard Terraform behavior. - instance CRUD is idempotent via standard Terraform behavior.
- subresource CRUD is idempotent by resource identity. - subresource CRUD is idempotent by resource identity.
- action operations are idempotent via a trigger field (e.g. run_id/nonce). - `redeploy`: idempotent via `git_revision` field — if unchanged, no redeploy
If the trigger does not change, the action is not re-run.
### Lifecycle Behavior ### Lifecycle Behavior
@@ -83,6 +109,13 @@ Each operation has a kind:
- Core is universal: no service-specific logic inside the core. - Core is universal: no service-specific logic inside the core.
- Generated service resources contain only schema/params and references. - 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 Resources
Subresource operations are exposed as standard resources. Subresource operations are exposed as standard resources.
@@ -98,14 +131,17 @@ resource "nubes_postgres_user" "user1" {
role = "app_user" 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: Example HCL:
resource "nubes_postgres_restart" "restart1" { resource "nubes_flask" "app" {
postgres_id = nubes_postgres.db.id resource_name = "my-flask"
run_id = "2026-02-20-001" git_revision = "abc123" # ← change this to trigger redeploy
app_configuration = jsonencode({...})
} }
## Concurrency and State Locking ## Concurrency and State Locking