TOOLS/ — generators + scripts + config + ARCHITECTURE.md ├── yaml-generator/ (code) ├── resource-generator/ (code) ├── docs-generator/ (code) ├── scripts/ (← devops/*.sh) ├── config/ (← devops/profiles/ + devops/config/) │ ├── test/ profile.env, services_list.txt, operation_timeouts.json │ ├── prod/ │ └── dev/ └── ARCHITECTURE.md (← devops/ARCHITECTURE.md) generated/ — pipeline output only (gitignored) ├── test/resources_yaml/, go/, docs/, provider_build/ ├── prod/ └── dev/ provider/ — code only, no generated files resources_yaml/ — DELETED (generated) internal/resources_gen/ — DELETED (generated) devops/ — removed (replaced by TOOLS/scripts + TOOLS/config + generated/) Generators now fail if NUBES_*_DIR not set (no defaults to provider/). Provider requires pipeline to populate resources_gen/ before build.
185 lines
7.3 KiB
Markdown
185 lines
7.3 KiB
Markdown
# 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. Any change to provider logic MUST be
|
|
reflected here FIRST, then implemented in `gen_v2` and other tools.
|
|
|
|
## Core Principles
|
|
|
|
1) YAML per service is generated ONLY from API data.
|
|
2) The provider core is universal and must not contain service-specific logic.
|
|
3) Service-specific Go code is fully generated from YAML. No manual edits.
|
|
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
|
|
|
|
- The inclusion list is defined by: `devops/config/services_list.txt` (repo-relative path)
|
|
- 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:
|
|
- Provider code generation
|
|
- Documentation generation
|
|
- Validation rules
|
|
|
|
Required top-level fields:
|
|
- name
|
|
- service_id
|
|
- service_display_name
|
|
- service_short_name
|
|
- lifecycle
|
|
- outputs
|
|
- operations
|
|
- service_man
|
|
|
|
### Operations: Kinds and Rules
|
|
|
|
Each operation has a kind:
|
|
|
|
**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.
|
|
- `redeploy`: idempotent via `git_revision` field — if unchanged, no redeploy
|
|
|
|
### Lifecycle Behavior
|
|
|
|
- For `instance` resources with `suspend`/`resume`, use explicit flags:
|
|
`adopt_existing_on_create` (default `false`) and `suspend_on_destroy` (default `true`).
|
|
- Apply decision matrix for suspend-capable services:
|
|
- cloud status `missing` or `deleted` -> `create`
|
|
- `suspend` + `adopt_existing_on_create=true` + key params match -> `resume` + `adopt`
|
|
- `suspend` + `adopt_existing_on_create=false` -> error (explicitly require flag for resume/adopt)
|
|
- `suspend` + key params mismatch -> error
|
|
- `running` + `adopt_existing_on_create=true` -> adopt/import behavior
|
|
- `running` + `adopt_existing_on_create=false` -> error
|
|
- `not created` -> error, no auto-adopt/create
|
|
- `creating`/`pending`/`failed` -> error
|
|
- Conflict diagnostics requirement:
|
|
- When `resource_name` already exists and `adopt_existing_on_create=false`, diagnostics must explicitly offer two choices:
|
|
1) change `resource_name` to create a new resource;
|
|
2) import/adopt existing one by setting `adopt_existing_on_create=true` and re-running `apply`.
|
|
- Destroy behavior for suspend-capable services:
|
|
- `suspend_on_destroy=true` -> call `suspend`
|
|
- `suspend_on_destroy=false` -> remove from Terraform state only (no API call)
|
|
|
|
### Diagnostics Format
|
|
|
|
- Lifecycle diagnostics for plan/apply must be multiline and human-readable.
|
|
- Include decision reason and controlling flag in message body.
|
|
- Print details as separate lines: `resource_name`, `service_id`, `instance_uid`,
|
|
`status`, `status_raw`, `operation_pending`, `operation_in_progress`.
|
|
|
|
## Provider Model
|
|
|
|
- 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.
|
|
Example mapping:
|
|
- create_user/delete_user/modify_user => nubes_<service>_user
|
|
- create_database/delete_database => nubes_<service>_database
|
|
|
|
Example HCL:
|
|
|
|
resource "nubes_postgres_user" "user1" {
|
|
postgres_id = nubes_postgres.db.id
|
|
username = "app_user"
|
|
role = "app_user"
|
|
}
|
|
|
|
### Redeploy (inline action)
|
|
|
|
Services with `redeploy` operation get a `git_revision` field in the main resource.
|
|
Changing `git_revision` triggers `redeploy` instead of `modify`.
|
|
|
|
Example HCL:
|
|
|
|
resource "nubes_flask" "app" {
|
|
resource_name = "my-flask"
|
|
git_revision = "abc123" # ← change this to trigger redeploy
|
|
app_configuration = jsonencode({...})
|
|
}
|
|
|
|
## Concurrency and State Locking
|
|
|
|
**Provider-level guarantees:**
|
|
- The provider does NOT implement distributed locking for instance operations.
|
|
- Two concurrent `terraform apply` with the same `resource_name` may create duplicate
|
|
instances, leading to a «multiple instances found» error on subsequent applies.
|
|
|
|
**User responsibility:**
|
|
- Use Terraform backend with state locking (S3+DynamoDB, etc.).
|
|
- Do NOT run `terraform apply` from two workspaces against the same state simultaneously.
|
|
- If duplicates occur: delete extras via Cloud Console and re-apply.
|
|
|
|
**API-side limitations:**
|
|
- Nubes API does not enforce unique `displayName` per serviceId.
|
|
- The provider cannot atomically guarantee «create-or-adopt» without API support
|
|
for conditional creation or name uniqueness constraints.
|
|
|
|
## Documentation Model
|
|
|
|
From the unified YAML, generate:
|
|
- Resource page: CRUD params, outputs, lifecycle defaults, operations summary
|
|
- MAN page: service_man + parameter man blocks
|
|
- Resources index: each resource links to its page and its MAN page
|
|
|
|
## Pipeline Overview (DevOps)
|
|
|
|
1) Generate unified YAML from API for services_list.txt.
|
|
2) Generate provider code from YAML.
|
|
3) Generate documentation from YAML.
|
|
4) Build provider for linux/windows/darwin.
|
|
5) Upload provider artifacts to registry.
|
|
6) Build and publish docs to site.
|
|
|
|
## Non-Negotiable Rules
|
|
|
|
- No manual edits to generated YAML or generated Go code.
|
|
- Any change must come from API or generator logic updates.
|
|
- The generator must enforce these rules and fail fast on drift.
|