Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/skills/connector-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Collect the current state of all connectors across Argo, Airbyte, and ClickHouse

## Prerequisites

- `KUBECONFIG` set to target cluster (e.g. `access/virtuozzo/cyber-insight-k8s.kubeconfig`)
- `KUBECONFIG` set to target cluster
- Airbyte API accessible via port-forward on `localhost:8001` (or already forwarded)
- `kubectl` access to namespaces: `argo`, `airbyte`, `data`

Expand Down
20 changes: 10 additions & 10 deletions .env.virtuozzo.example → .env.remote.example
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
# Insight — Virtuozzo (remote OpenStack-Magnum cluster).
# Insight — remote cluster with pre-existing infrastructure.
#
# Copy to .env.virtuozzo (gitignored) and fill in real values.
# Copy to .env.remote (gitignored) and fill in real values.
#
# One-time setup:
# 1. Images are built and pushed by CI on merge to main (see .github/workflows/build-images.yml).
# Tags follow YYYY.MM.DD.HH.mm-<sha> format. Set IMAGE_TAG below to pin a version.
# For manual builds: re-run with BUILD_AND_PUSH=true.
# 2. Create Okta SPA application (PKCE, no client_secret), register redirect URI:
# http://<vpn-node-ip>/callback e.g. http://192.168.44.36/callback
# http://<node-ip>/callback
# 3. Apply insight-oidc Secret (see src/backend/services/api-gateway/secrets/oidc.yaml.example)
#
# Typical deploy — додеплой недостающего без переразвертывания существующего:
# ./up.sh --env virtuozzo app
# Typical deploy — install missing components without redeploying existing ones:
# ./dev-up.sh --env remote app
# This deploys: ingress-nginx, redis, api-gateway, analytics-api, identity, frontend.
# It does NOT touch existing: mariadb, clickhouse, airbyte, argo, metabase.

# ── Cluster ────────────────────────────────────────────────────
CLUSTER_MODE=remote
NAMESPACE=insight
KUBECONFIG=access/virtuozzo/cyber-insight/cyber-insight-k8s-31_03_2026_18_03.kubeconfig
KUBECONFIG=path/to/cluster.kubeconfig

# ── Images ─────────────────────────────────────────────────────
IMAGE_REGISTRY=ghcr.io/constructorfabric
Expand All @@ -29,9 +29,9 @@ IMAGE_PULL_POLICY=Always
# ANALYTICS_API_IMAGE_TAG=2026.04.21.14.30-abc1234
# IDENTITY_IMAGE_TAG=2026.04.21.14.30-abc1234
# TOOLBOX_IMAGE_TAG=2026.04.21.14.30-abc1234
# Target platform for the cluster. On an Apple Silicon workstation this
# triggers buildx + QEMU emulation (slow but needed because Virtuozzo nodes
# are amd64). When IMAGE_PLATFORM is set, buildx pushes directly (no local load).
# Target platform for the cluster. On an Apple Silicon workstation building
# for amd64 nodes this triggers buildx + QEMU emulation (slow but required).
# When IMAGE_PLATFORM is set, buildx pushes directly (no local load).
IMAGE_PLATFORM=linux/amd64

# For routine deploys when image is already published, set both to false.
Expand All @@ -50,7 +50,7 @@ INGRESS_HTTPS_PORT=443
INGRESS_ENABLED=true # turn on chart-level Ingress resources
INGRESS_CLASS=nginx

# ── Infra: додеплой недостающего ───────────────────────────────
# ── Infra: deploy only what is missing ─────────────────────────
# mariadb, clickhouse, airbyte, argo already exist — DO NOT re-deploy.
# Redis is missing in the cluster but required by analytics-api.
DEPLOY_REDIS=true
Expand Down
2 changes: 1 addition & 1 deletion DEVLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**Purpose**: full narrative of the first-run debugging of `./dev-up.sh` on a clean Kind cluster, after the umbrella / canonical-installer refactor (PR #224).

**Worktree**: `/Users/roman/alemira/insight/.claude/worktrees/laughing-feistel-fe1e16`
**Worktree**: `.claude/worktrees/laughing-feistel-fe1e16`
**Branch**: `claude/laughing-feistel-fe1e16`
**Start**: 2026-04-23

Expand Down
2 changes: 1 addition & 1 deletion dev-restart.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#
# Usage:
# ./dev-restart.sh # default: --env local
# ./dev-restart.sh --env virtuozzo # remote env (just verify connectivity)
# ./dev-restart.sh --env remote # remote env (just verify connectivity)
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
Expand Down
8 changes: 4 additions & 4 deletions dev-up.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Insight platform — DEV bring-up from source.
#
# Use this when you work on the codebase: builds Docker images from src/,
# creates a local Kind cluster (or targets a dev-owned remote like virtuozzo),
# creates a local Kind cluster (or targets a dev-owned remote cluster),
# loads images into the cluster, and deploys all services.
#
# NOT for end-user installations. For customers / production-like installs
Expand All @@ -21,8 +21,8 @@
#
# Usage:
# ./dev-up.sh # --env local, all components
# ./dev-up.sh --env virtuozzo # remote cluster, all components
# ./dev-up.sh --env virtuozzo app # backend + frontend only
# ./dev-up.sh --env remote # remote cluster, all components
# ./dev-up.sh --env remote app # backend + frontend only
# ./dev-up.sh ingestion # only ingestion (default env=local)
#
# Valid components: all | ingestion | app | backend | frontend
Expand Down Expand Up @@ -565,7 +565,7 @@ if [[ "$CLUSTER_MODE" == "local" || "${SHOW_CREDS:-}" == "1" ]]; then
[[ -n "$MDB_ROOT" ]] && echo " MariaDB root / $MDB_ROOT"
[[ -n "$REDIS_PASS" ]] && echo " Redis default / $REDIS_PASS"
else
# For remote envs (virtuozzo, prod-like) — DO NOT dump passwords into
# For remote (prod-like) envs — DO NOT dump passwords into
# the developer's terminal scrollback. Print only how to fetch them.
echo " (remote cluster — passwords not printed; pass SHOW_CREDS=1 to override)"
echo " Fetch with:"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ How do we represent the *intended* connector version durably and unambiguously,
- **No recreate on version bump**: when the operator bumps the version, sources and connections must NOT be deleted and recreated by default — sync state (Airbyte cursors per stream) is precious.
- **Human-editable**: the version anchor must live in version-controlled source code so PR review and `git blame` apply.
- **Low operational overhead**: no extra storage system to provision, monitor, back up, or recover.
- **Drift-resistant**: the anchor must not depend on a parallel local state that can desynchronize from cluster reality (this is the failure mode of `state.yaml` and `airbyte-state` ConfigMap on the virtuozzo cluster as of 2026-05-04).
- **Drift-resistant**: the anchor must not depend on a parallel local state that can desynchronize from cluster reality (the observed failure mode of `state.yaml` and the `airbyte-state` ConfigMap in production).

## Considered Options

Expand Down Expand Up @@ -76,7 +76,7 @@ A YAML file checked into the repo, containing `applied_version` per connector al
- Good, because zero new infrastructure.
- Neutral, because requires a strict contract between writer (CI/CD) and reader (operators).
- Bad, because the writer must commit and push for the file to reflect reality; manual recovery edits or out-of-band Airbyte changes leave the file inconsistent.
- Bad, because in observed production (virtuozzo cluster) the file already drifted heavily from Airbyte — UUIDs in the file no longer existed in the cluster.
- Bad, because in observed production the file already drifted heavily from Airbyte — UUIDs in the file no longer existed in the cluster.

### Option B — Cluster-side `ConfigMap airbyte-state`

Expand Down Expand Up @@ -115,7 +115,6 @@ A strict semver `MAJOR.MINOR.PATCH` string in `descriptor.yaml`; on publish, the
- `cpt-insightspec-adr-cdk-prebuilt-images` (ADR-0011) — CDK image identity carried verbatim in `descriptor.cdk_image`; reconcile splits and applies; never builds at runtime.
- `cpt-insightspec-adr-enrich-image-in-descriptor` (ADR-0014) — same single-source-of-truth principle extended to enrich sidecar images (jira-enrich, future youtrack-enrich).
- `cpt-insightspec-adr-semver-and-full-refresh` (ADR-0015) — strict semver `MAJOR.MINOR.PATCH` format; any bump triggers catalog re-discover so new streams/fields are auto-enabled; major bump additionally dispatches a one-shot `dbt --full-refresh` for that connector's `dbt_select` scope. No cross-connector cascade.
- Background — original investigation against virtuozzo cluster (2026-05-04): `state.yaml` `definitions.{connector}.id` did not match any source's `sourceDefinitionId` for 8 of 9 connectors; clear evidence the parallel-store approach had failed.

## Traceability

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ decision-makers: platform-engineering
**ID**: `cpt-insightspec-adr-adoption-of-existing-resources`
## Context and Problem Statement

Legacy clusters (e.g., virtuozzo, observed 2026-05-04) already have running Airbyte sources and connections that were created by the pre-refactor `register.sh` and `connect.sh` scripts. Each connection has accumulated Airbyte sync state (per-stream cursors). The new reconcile engine introduced by ADR-0001 expects each connection to carry an `insight` membership tag plus a `cfg-hash:<sha256(secret.data)>` tag, and each definition to carry the descriptor version in its `description` field — none of which exist on legacy resources.
Clusters provisioned before this refactor already have running Airbyte sources and connections that were created by the pre-refactor `register.sh` and `connect.sh` scripts. Each connection has accumulated Airbyte sync state (per-stream cursors). The new reconcile engine introduced by ADR-0001 expects each connection to carry an `insight` membership tag plus a `cfg-hash:<sha256(secret.data)>` tag, and each definition to carry the descriptor version in its `description` field — none of which exist on legacy resources.

How do we bring legacy resources under the new declarative model **without** recreating any source or connection — i.e., without losing sync state and without forcing a full historical re-fetch?

Expand Down Expand Up @@ -81,7 +81,7 @@ Delete every existing source and connection; let the new reconcile pass create t

### Option B — Rename to canonical pattern with version-encoded names

Rename existing sources and connections to a name pattern that encodes the descriptor version, e.g., `bamboohr-bamboohr-main-virtuozzo-v2026.05.04`.
Rename existing sources and connections to a name pattern that encodes the descriptor version, e.g., `bamboohr-bamboohr-main-acme-v2026.05.04`.

- Good, because metadata stays inside the resource itself.
- Neutral, because Airbyte does not strongly index by name — querying "all our resources" becomes a substring scan.
Expand All @@ -102,7 +102,7 @@ Rename existing sources and connections to a name pattern that encodes the descr
## More Information

- Sequence: `cpt-insightspec-seq-adopt-one-shot` in `DESIGN.md` §3.6 details the API call order.
- Tag verification (2026-05-04, virtuozzo): `POST /api/public/v1/tags` to create the `insight` tag, `PATCH /api/public/v1/connections/{id}` to attach, `GET /api/public/v1/connections?tagIds=<id>` to query — all three round-tripped successfully.
- Tag verification against a live Airbyte instance: `POST /api/public/v1/tags` to create the `insight` tag, `PATCH /api/public/v1/connections/{id}` to attach, `GET /api/public/v1/connections?tagIds=<id>` to query — all three round-tripped successfully.
- Related decisions:
- `cpt-insightspec-adr-version-driven-reconcile` (ADR-0001) — provides the version anchor that adoption sets.
- `cpt-insightspec-adr-credential-rotation-no-env` (ADR-0003) — explains why `cfg-hash` is computed from K8s Secret, not from Airbyte's masked credential view.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Chosen option: **Option C — `sources/update` on cfg-hash mismatch**.

### Consequences

- **Good**, because no infrastructure addition: works on virtuozzo and any future cluster as-is.
- **Good**, because no infrastructure addition: works on any cluster as-is.
- **Good**, because state is preserved: `sources/update` is documented to keep the source's `sourceId` and the connection's `connectionId`.
- **Good**, because rotation cost is one API call per rotated Secret, not a full reconcile cycle.
- **Good**, because the rotation event is observable via Airbyte's audit logs (`sources/update` is a discrete, logged operation).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ Chosen option: **Option C — `ConfigMap insight-config` with env override**.

### Confirmation

- `kubectl -n "${INSIGHT_NAMESPACE}" get configmap insight-config -o jsonpath='{.data.tenant_id}'` returns the cluster's tenant ID; reconcile uses the value without falling back. `INSIGHT_NAMESPACE` matches the Helm release namespace (e.g. `data` on virtuozzo, `insight` on dev) and is supplied to the runtime by the chart via the pod env; no namespace literal is hard-coded by reconcile.
- `kubectl -n "${INSIGHT_NAMESPACE}" get configmap insight-config -o jsonpath='{.data.tenant_id}'` returns the cluster's tenant ID; reconcile uses the value without falling back. `INSIGHT_NAMESPACE` matches the Helm release namespace (e.g. `insight` on dev; customer clusters may differ) and is supplied to the runtime by the chart via the pod env; no namespace literal is hard-coded by reconcile.
- Setting `INSIGHT_TENANT_ID=test-tenant` and invoking `reconcile-connectors.sh --dry-run` resolves the env value, ignoring any ConfigMap content (precedence test).
- Removing the ConfigMap and unsetting the env var causes `reconcile-connectors.sh` to abort with a clear message ("`tenant_id` not configured: set `INSIGHT_TENANT_ID` or create `ConfigMap insight-config`").

## Pros and Cons of the Options

### Option A — `connections/<tenant>.yaml` in repo

A YAML file per cluster (e.g., `connections/virtuozzo.yaml`) with `tenant_id: virtuozzo`. The toolkit reads from a default-named file or via `--tenant <name>`.
A YAML file per cluster (e.g., `connections/acme.yaml`) with `tenant_id: acme`. The toolkit reads from a default-named file or via `--tenant <name>`.

- Good, because `tenant_id` is version-controlled and reviewable.
- Neutral, because the file's content (only `tenant_id`) is sparse — historically also held destination overrides that are now redundant.
Expand Down Expand Up @@ -112,7 +112,7 @@ Cluster-level ConfigMap in the Insight Helm release namespace (resolved at runti
name: insight-config
namespace: <insight-namespace>
data:
tenant_id: virtuozzo
tenant_id: acme
```
- Resolution precedence (toolkit): `INSIGHT_TENANT_ID` env var (if set and non-empty) → `ConfigMap insight-config.data.tenant_id` (looked up in `${INSIGHT_NAMESPACE}`) → abort.
- Related decisions:
Expand Down
4 changes: 2 additions & 2 deletions docs/components/airbyte-toolkit/specs/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -893,8 +893,8 @@ The reconcile engine identifies resources by deterministic conventions, not by s
| Airbyte `connection.tags` | membership + config hash | `["insight", "cfg-hash:<sha256(secret.data)>"]` | Membership marker + per-instance config drift detector |
| K8s Secret label | membership | `app.kubernetes.io/part-of=insight` | Discovery filter |
| K8s Secret annotations | identity | `insight.cyberfabric.com/connector=<name>`, `insight.cyberfabric.com/source-id=<id>` | Pair Secret with `connectors/<name>/descriptor.yaml` and Airbyte source name |
| Airbyte `source.name` | composed | `{connector_name}-{source_id}-{tenant_id}` | Stable lookup pattern (e.g., `bamboohr-bamboohr-main-virtuozzo`) |
| Airbyte `connection.name` | composed | `{connector_name}-{source_id}-to-clickhouse-{tenant_id}` | Stable lookup pattern (e.g., `bamboohr-bamboohr-main-to-clickhouse-virtuozzo`) |
| Airbyte `source.name` | composed | `{connector_name}-{source_id}-{tenant_id}` | Stable lookup pattern (e.g., `bamboohr-bamboohr-main-acme`) |
| Airbyte `connection.name` | composed | `{connector_name}-{source_id}-to-clickhouse-{tenant_id}` | Stable lookup pattern (e.g., `bamboohr-bamboohr-main-to-clickhouse-acme`) |
| Airbyte `connection.namespaceFormat` | bronze database | `bronze_{connector_name_underscored}` | Per-connector ClickHouse Bronze database |

> **Tenant resolution**: `tenant_id` comes from cluster-level `ConfigMap insight-config` (data field `tenant_id`) or env var `INSIGHT_TENANT_ID` as fallback. Per-tenant `connections/<tenant>.yaml` files are removed (Decision #6).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Conventions:
## 0. Inputs and ground truth

- **Reference manifest** (Airbyte declarative source v0.57.0, incompatible with current repo):
`/Users/roman/alemira/insight/zulip_proxy.yaml` — Bearer auth against a proxy that aggregates
`zulip_proxy.yaml` (local workspace copy, not in repo) — Bearer auth against a proxy that aggregates
Zulip data. Streams `users` (offset-paginated) and `messages` (cursor-paginated, incremental on
`created_at`).
- **Same-data sibling spec** (existing Zulip Basic-Auth connector docs in the repo):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ contribution.
- **Skill workflows**: `cypilot/.core/skills/connector/workflows/create.md`,
`cypilot/.core/skills/connector/workflows/test.md`,
`cypilot/.core/skills/connector/workflows/validate.md`
- **Reference manifest** (incompatible 0.57.0): `/Users/roman/alemira/insight/zulip_proxy.yaml`
- **Reference manifest** (incompatible 0.57.0): `zulip_proxy.yaml` (local workspace copy, not in repo)
- **Closest existing connector for dbt patterns**: `src/ingestion/connectors/collaboration/zoom/`
- **Dependencies**: `promote_bronze_to_rmt`, `identity_inputs_from_history`, `snapshot`,
`fields_history` macros (already exist in `src/ingestion/dbt/macros/`).
Expand Down
Loading
Loading