Commit 1ed21274 authored by Christos Tranoris's avatar Christos Tranoris
Browse files

adding docs indexes

parent 06471dca
Loading
Loading
Loading
Loading
Loading
+80 −0
Original line number Diff line number Diff line
# CRIDGE support for OSOM's tentative-planning simulation contract

## What changed

CRIDGE now answers OSOM's "tentative planning" (what-if) dry-run requests instead of timing out. When
an operator runs a simulated plan with `simulateAdapters=true`, OSOM's `CROrchestrationService` /
`PlanningDay2AdapterSimulator` send deploy/patch/delete requests to CRIDGE's `/PLANNING`-suffixed
queues (`CRD.DEPLOY.CR_REQ/PLANNING`, `CRD.PATCH.CR_REQ/PLANNING`, `CRD.DELETE.CR_REQ/PLANNING`) rather
than the production queues. Previously nothing consumed those queues, so every simulated plan involving
a CR-backed service reported CRIDGE as unreachable (`AdapterInvocationException`, `NO_REPLY`) after
~5s, and the owning service showed as failed.

This is a pure addition: a new `org.etsi.osl.cridge.simulation` package with its own routes and beans.
**No production class (`CRRouteBuilder`, `KubernetesClientResource`) or production queue property was
modified.** Production traffic, queues, and behavior are unchanged.

## New files

- `src/main/java/org/etsi/osl/cridge/simulation/CRPlanningRouteBuilder.java` — Camel routes on the
  three `_PLANNING` queue properties, mirroring `CRRouteBuilder`'s shape, each delegating to
  `SimulatedKubernetesClientResource`.
- `src/main/java/org/etsi/osl/cridge/simulation/SimulatedKubernetesClientResource.java`
  `deployCR`/`patchCR`/`deleteCR(headers, crspec)`, same method shapes as production's
  `KubernetesClientResource` for symmetry, but never touches `KubernetesClient` or the real cluster.
  Validates the CR spec is well-formed (fabric8 `Serialization.unmarshal`), replies synchronously with
  production's own string contract (`"OK"` / `"FAIL <msg>"` / `"ERROR <msg>"`), then asynchronously
  reports resource status/estimates back to OSOM. Deliberately reproduces a pre-existing production
  naming quirk (`org.etsi.osl.prefixName` is checked under the wrong key, `prefixId`, in
  `deployCR`/`patchCR`, so both always fall through to `"cr" + resourceId.substring(0,8)`) rather than
  "fixing" it, so simulated names stay consistent with what production actually assigns today.
- `src/main/java/org/etsi/osl/cridge/simulation/PlanningCallbackClient.java` — sends the async
  "planning resource" callback (`PLANNING.RESOURCE.CREATEORUPDATE`) that actually populates OSOM's
  plan: resource name/status and cost/duration/energy estimates, using the exact header constants from
  OSOM's `PlanningResourceCallbackHandler`. Omits unmeasured estimate components rather than
  zero-padding them (an omitted component means "not measured"; an explicit zero means "measured as
  zero" — the two are not interchangeable per the OSOM contract).

## Config additions (`application.yml`)

- Four new queue properties: `CRD_DEPLOY_CR_REQ_PLANNING`, `CRD_PATCH_CR_REQ_PLANNING`,
  `CRD_DELETE_CR_REQ_PLANNING`, `PLANNING_RESOURCE_CREATEORUPDATE`.
- A `cridge.simulation.*` block of tunables: simulated deploy delay, a local default operation duration
  (OSOM never sends CRIDGE an `org.etsi.osl.operationDurationSeconds` header, unlike the
  generic-controller path, so this falls back to 86400s — mirroring OSOM's own
  `tentative.planning.default-operation-duration-seconds` default), and static placeholder
  creation/operation/teardown energy and cost figures (clearly labelled as placeholders, not measured
  data).

## Scoping decisions made with the user

- New consumers live in the same CRIDGE process (new routes/beans), not a separate service.
- The missing `operationDurationSeconds` header is defaulted locally to 86400s rather than blocking on
  an OSOM-side fix.
- The first version reports static placeholder energy/cost figures alongside duration/status, rather
  than omitting them.

## Verification performed

- `mvn -DskipTests compile` — clean compile of the new package.
- Two new unit test classes (plain JUnit 5 + Mockito, no Spring context needed):
  - `SimulatedKubernetesClientResourceTest` — asserts the `"OK"`/`"FAIL ..."`/`"ERROR ..."` response
    contract, the `RESERVED``AVAILABLE` (create) / `AVAILABLE` (patch) / `UNKNOWN` (delete) status
    sequence, the reproduced naming quirk, and that a malformed CR spec fails cheaply without ever
    invoking the callback.
  - `PlanningCallbackClientTest` — asserts the actual header map sent to
    `PLANNING.RESOURCE.CREATEORUPDATE` carries the required `org.etsi.osl.serviceOrderId`, echoes
    `org.etsi.osl.resourceId`, and omits (never zero-pads) unmeasured estimate components; and that a
    callback with no `serviceOrderId` is skipped rather than sent.
- Full suite (`mvn test`): 15/15 tests green — `CridgeIntegrationTest` (6, unchanged) +
  `PlanningCallbackClientTest` (4, new) + `SimulatedKubernetesClientResourceTest` (5, new).

**Not performed in this session:** end-to-end verification against a live broker with both CRIDGE and
OSOM running (the plan's `curl`/`jq` checklist against `/replan?simulateAdapters=true`). That requires
a running OSOM + ActiveMQ/Artemis environment outside this repo checkout.

## Full plan

See `C:\Users\ctranoris\.claude\plans\fluffy-dancing-quasar.md` for the complete approved plan this
implementation followed, including the full wire-contract writeup (queues, headers, reply/callback
shapes) reverse-engineered from the OSOM source.
+50 −0
Original line number Diff line number Diff line
# CRIDGE simulation now honours OSOM's operationDurationSeconds header

## Correction to the previous change

[20260812_1347_simulation-support-for-cridge.md](20260812_1347_simulation-support-for-cridge.md) built
CRIDGE's simulation path on the assumption, confirmed at the time against OSOM source, that OSOM never
sent CRIDGE an `org.etsi.osl.operationDurationSeconds` header — so `SimulatedKubernetesClientResource`
always sized the operation-phase estimate from a local static default
(`cridge.simulation.default-operation-duration-seconds`, 86400s), ignoring the header entirely.

Later the same day, OSOM shipped
`docs/20260812_1545_operation-window-for-all-planning-sends.md`: every planning send to CRIDGE
(create/patch/delete, both simulated and `executeAdaptersForReal`) now carries this header, derived
once from the order's requested dates. The user flagged that CRIDGE's simulation code still ignored it.

## What changed

`SimulatedKubernetesClientResource`:
- Added `operationDurationSecondsFor(headers)`, which reads
  `org.etsi.osl.operationDurationSeconds` off the inbound request headers (tolerant of it arriving as a
  `String`, `Long`, etc. off a JMS/Camel exchange) and uses it for the operation-phase estimate.
- Falls back to the local `default-operation-duration-seconds` config value only when the header is
  genuinely absent (older OSOM, or a caller that doesn't send it) — matching how OSOM's own
  `OperationDurationResolver` falls back to its configured default.
- `scheduleCreateCallbacks` (deploy) and `schedulePatchCallback` (patch) now resolve this value per
  request instead of always using the static default.

`application.yml`: updated the `cridge.simulation.*` comment block to describe
`default-operation-duration-seconds` as the fallback it actually is, not the figure that's always used.

## Why this matters

Per OSOM's own writeup, aggregation across resources in a plan takes `max` of the operation durations
on the assumption they're all sized against the *same* order-derived number. A CRIDGE simulation that
kept inventing its own 86400s window regardless of what OSOM actually asked for would silently break
that assumption on any order whose requested dates implied a different window — the resource's
operate-phase energy/cost would be computed over the wrong duration.

## Tests

Extended `SimulatedKubernetesClientResourceTest` (now 7 tests, up from 5):
- `deployCR_wellFormedSpec_repliesOK_andReportsReservedThenAvailable` now also asserts the fallback
  default is used when no header is sent.
- New `deployCR_withOperationDurationSecondsHeader_usesHeaderWindow_notLocalDefault` — asserts a
  string-valued header (`"7200"`) is parsed and used verbatim, not the default.
- `patchCR_wellFormedSpec_repliesOK_andReportsAvailableOnce` now asserts the fallback default.
- New `patchCR_withOperationDurationSecondsHeader_usesHeaderWindow_notLocalDefault`.

Full suite: `mvn test` → 17/17 green (`CridgeIntegrationTest` 6, `PlanningCallbackClientTest` 4,
`SimulatedKubernetesClientResourceTest` 7).
+80 −0
Original line number Diff line number Diff line
# CRIDGE simulator fabricates a Secret resource for ArgoCD K8aaS Applications

## What changed

`SimulatedKubernetesClientResource.deployCR` now recognizes one CR shape specially: an ArgoCD
`Application` (`apiVersion: argoproj.io/v1alpha1`, `kind: Application`). This is OSL's K8aaS
(Kubernetes-as-a-Service) cluster-provisioning pattern — ArgoCD deploys a Terraform-controller-backed
Helm chart into `spec.destination.namespace` (in practice the service order id), which eventually
writes a kubeconfig-bearing Secret into that same namespace once the real cluster comes up.

In production, that eventual Secret is what `NamespaceWatcher` mirrors into the catalog
(`KubernetesClientResource.KubernetesSecret2OpensliceResource`) — but only once it actually exists on
a real cluster. A simulation never creates anything real, so nothing ever triggers that mirroring, and
a tentative plan for a K8aaS order would show only the `Application` resource, missing the Secret a
real deployment would also produce.

`deployCR` now fabricates that eventual outcome: when the incoming CR is an ArgoCD `Application`, it
schedules one **additional** planning-resource callback, alongside the ordinary two it already sends
for the CR itself, representing the Secret that would appear in `spec.destination.namespace`. For any
other CR kind, behavior is unchanged.

## How it's implemented

- `wellFormed(String)` was split: the new `parseCrSpec(String)` returns the parsed
  `GenericKubernetesResource` (or `null`) so `deployCR` can inspect `apiVersion`/`kind`/`spec` instead
  of discarding the parse result; `wellFormed` is now a one-line wrapper over it for the callers
  (`patchCR`) that only need the yes/no answer.
- `scheduleArgoCdSecretCallbackIfApplicable(headers, gkr)`: matches `apiVersion`/`kind` exactly, reads
  `spec.destination.namespace` via fabric8's `gkr.get("spec", "destination", "namespace")` nested-path
  accessor (skips silently, with a debug log, if that path is absent), and — after the same simulated
  deploy delay as the Application's own availability callback — calls
  `planningCallbackClient.report(...)` with:
  - **`resourceId = null`**: per OSOM's `PlanningResourceCallbackHandler`, a callback with no
    `org.etsi.osl.resourceId` always creates a *new* `ExpectedResource` in the plan (linked to the
    owning `ExpectedService` via `serviceId`) rather than updating an existing row — exactly what's
    needed for a resource distinct from the Application itself.
  - **name**: `"{applicationName}-secret@{destinationNamespace}[@{currentContextCluster}[@{clusterMasterURL}]]"`,
    mirroring production's real secret-naming shape
    (`"{secretName}@{namespace}@{cluster}@{masterURL}"`) as closely as simulation can. The real
    secret's own name is opaque until the Terraform-controller chart actually creates it, so the
    Application's own `metadata.name` is the best available stand-in; the cluster/masterURL segments
    are included only when those (unprefixed) headers are present on the request.
  - **status**: `AVAILABLE`, matching production — a mirrored `KubernetesSecret` always reports
    `AVAILABLE` (`KubernetesSecret.toResourceCreate()` hardcodes it), it never goes through a reserved
    phase.
  - **estimates**: all omitted (not zero-padded). A mirrored Secret carries no cost/duration/energy of
    its own in production; it's a passive artifact of the cluster resource, not separately billable.
- **Scope: `deployCR` only**, deliberately not `patchCR`/`deleteCR`. A callback with no `resourceId`
  always creates a *new* resource, so firing this on every `patchCR` of the same Application would
  invent a duplicate secret row on every re-plan rather than updating the one already there.
  `deleteCR` is left alone too — the planning callback contract is create/update only, there's no
  teardown signal for a resource that was never really tracked to begin with.

No changes to `PlanningCallbackClient`, `CRPlanningRouteBuilder`, `application.yml`, or any production
class — this is confined entirely to `SimulatedKubernetesClientResource`.

## Tests

Added to `SimulatedKubernetesClientResourceTest` (now 9, up from 7):
- `deployCR_nonArgoCdSpec_doesNotReportAnAdditionalSecretResource` — the existing crontab fixture
  still produces exactly the two ordinary callbacks and nothing more
  (`verifyNoMoreInteractions`), pinning "otherwise perform the existing behaviour".
- `deployCR_argoCdApplication_alsoReportsAdditionalSecretResourceForDestinationNamespace` — using a
  new fixture `src/test/resources/argocd-application-cr.yaml` (the exact K8aaS example CR), asserts
  the two ordinary callbacks plus exactly one additional `resourceId == null`, `AVAILABLE` callback
  with the expected `"k8s-cluster-secret@f4debc53-...@my-cluster@https://k8s.example.com:6443"` name
  and no estimates, and nothing further.

Full suite (as of this change): `mvn test` → 19/19 green (`CridgeIntegrationTest` 6,
`PlanningCallbackClientTest` 4, `SimulatedKubernetesClientResourceTest` 9). The additional-secret
callback's body was later enriched with `resourceCharacteristic`s — see
[20260814_1230_simulated-secret-resource-characteristics.md](20260814_1230_simulated-secret-resource-characteristics.md)
for the updated (20/20) count.

## Related

Builds on [20260812_1347_simulation-support-for-cridge.md](20260812_1347_simulation-support-for-cridge.md)
and [20260812_1700_honor-operation-duration-header-in-simulation.md](20260812_1700_honor-operation-duration-header-in-simulation.md).
Followed up by [20260814_1230_simulated-secret-resource-characteristics.md](20260814_1230_simulated-secret-resource-characteristics.md)
and [20260814_1300_scope-argocd-secret-simulation-to-k8aas-chart.md](20260814_1300_scope-argocd-secret-simulation-to-k8aas-chart.md).
+68 −0
Original line number Diff line number Diff line
# Simulated ArgoCD Secret callback now carries structural resourceCharacteristics

## What changed

The additional planning-resource callback that
[20260812_1830_argocd-application-secret-simulation.md](20260812_1830_argocd-application-secret-simulation.md)
introduced (fired for ArgoCD K8aaS `Application` CRs, representing the Secret that would eventually
land in `spec.destination.namespace`) previously reported only a bare name and `AVAILABLE` status. It
now also attaches a `resourceCharacteristic` list to the callback's `ResourceCreate` body, so a
tentative plan shows a Secret resource shaped like a real catalog entry — with namespace, cluster,
`Kind`, etc. — rather than just a name.

This was prompted by being handed three real TMF `Resource` JSON documents pulled from OSL's actual
catalog (two `Secret`-category resources, one `ConfigMap`-category resource) as the target shape to
approximate. Two decisions on scope, made explicitly before writing any code:

- **Wire it into the CRIDGE simulator directly** (enrich the existing callback), rather than producing
  static example files elsewhere.
- **Structural metadata only** — namespace, cluster, `Kind`, apiGroup, the `org.etsi.osl.*` ids. The
  real examples' `data`, `json`, and `metadata` characteristics carry the secret's actual (or
  base64-encoded) payload — a kubeconfig, an SSH key pair. A simulation creates nothing real, so there
  is no genuine payload to report; fabricating credential-shaped placeholder content there would be
  actively misleading (indistinguishable from a real secret at a glance) rather than a harmless
  placeholder. `UID` is omitted for the same reason — a real Secret's UID is assigned by the Kubernetes
  API server at creation time, so simulation has nothing honest to put there.

## How it's implemented

- **`PlanningCallbackClient.report(...)` gained a second, 15-argument overload** taking a trailing
  `Map<String, String> characteristics` (name → value); the original 14-argument signature is now a
  one-line delegator that passes `null` (unchanged behavior for every other caller). When present, each
  non-blank entry is added to the `ResourceCreate` via `addResourceCharacteristicItemShort(name, value,
  EValueType.TEXT.getValue())` — the same method production's own `KubernetesSecret.toResourceCreate()`
  uses, so the resulting JSON shape (`{"name": ..., "value": {"value": ..., "alias": null}}`) matches
  production's real characteristics exactly. Blank values are dropped rather than sent as empty
  characteristics.
- **`SimulatedKubernetesClientResource.secretResourceCharacteristicsFor(headers, namespace)`** builds
  the map: always `Kind=Secret`, `apiGroup=secrets`, `org.etsi.osl.namespace=<destination namespace>`;
  conditionally (only when present on the inbound request) `currentContextCluster`, `clusterMasterURL`,
  `org.etsi.osl.serviceOrderId`, `org.etsi.osl.serviceId`, `org.etsi.osl.prefixName`. Wired into
  `scheduleArgoCdSecretCallbackIfApplicable`, which now calls the new 15-arg `report(...)` overload.

No other CRIDGE behavior changed — this only enriches the one existing simulated-secret callback's
payload; the reply contract, route wiring, and every other simulated CR kind's behavior are untouched.

## Tests

- `PlanningCallbackClientTest.report_withCharacteristics_addsThemAsResourceCharacteristicsOnTheBody`
  (new): calls the 15-arg overload directly, parses the JSON body sent to
  `PLANNING.RESOURCE.CREATEORUPDATE`, and asserts the `resourceCharacteristic` array's `name`/`value.value`
  pairs match what was passed in, and that a blank value is dropped rather than sent.
- `SimulatedKubernetesClientResourceTest.deployCR_argoCdApplication_alsoReportsAdditionalSecretResourceForDestinationNamespace`
  (updated): its second `verify(...)` now targets the 15-arg overload (Mockito treats the two
  overloads as distinct mockable methods, so the previous 14-arg-shaped verify silently stopped
  matching once production switched overloads) and captures the characteristics map, asserting
  `Kind=Secret`, `apiGroup=secrets`, `org.etsi.osl.namespace`, `currentContextCluster`,
  `clusterMasterURL`, and the three `org.etsi.osl.*` id fields from the test's headers, while also
  asserting `data`/`json`/`UID` are absent.

Full suite: `mvn test` → 20/20 green (`CridgeIntegrationTest` 6, `PlanningCallbackClientTest` 5,
`SimulatedKubernetesClientResourceTest` 9) — up from 19, `PlanningCallbackClientTest` gaining the one
new test.

## Related

Builds on [20260812_1830_argocd-application-secret-simulation.md](20260812_1830_argocd-application-secret-simulation.md),
[20260812_1347_simulation-support-for-cridge.md](20260812_1347_simulation-support-for-cridge.md), and
[20260812_1700_honor-operation-duration-header-in-simulation.md](20260812_1700_honor-operation-duration-header-in-simulation.md).
+46 −0
Original line number Diff line number Diff line
# Simulated ArgoCD Secret callback now scoped to the K8aaS provisioning chart

## What changed

The additional planning-resource callback (see
[20260812_1830_argocd-application-secret-simulation.md](20260812_1830_argocd-application-secret-simulation.md))
previously fired for **any** ArgoCD `Application` CR that carried a `spec.destination.namespace`. That
was too broad: an `Application` deploying an ordinary workload chart never provisions a cluster and so
never produces a kubeconfig Secret as a side effect — fabricating one for it would be wrong, not merely
imprecise.

`SimulatedKubernetesClientResource` now also requires `spec.source.chart` to equal
`provision-k8s-cluster-argocd-tf-controller` (the K8aaS cluster-provisioning chart from the original
example) before scheduling the additional Secret callback. Every other ArgoCD `Application` — same
`apiVersion`/`kind`, different chart — now falls through to the ordinary two-callback behavior, same as
any other CR kind.

## How it's implemented

- New constant `ARGOCD_K8AAS_CHART = "provision-k8s-cluster-argocd-tf-controller"`.
- New `isProvisionK8sClusterChart(GenericKubernetesResource gkr)`, reading `spec.source.chart` via
  fabric8's nested-path accessor (`gkr.get("spec", "source", "chart")`) — matching the actual parsed
  field rather than a raw-text search over `crspec`, so it's robust to YAML/JSON formatting differences
  (quoting, spacing) a plain substring search would be sensitive to.
- `scheduleArgoCdSecretCallbackIfApplicable` now guards on `isArgoCdApplication(gkr) &&
  isProvisionK8sClusterChart(gkr)` instead of `isArgoCdApplication(gkr)` alone.

No other behavior changed.

## Tests

Added `deployCR_argoCdApplicationWithDifferentChart_doesNotReportAnAdditionalSecretResource` to
`SimulatedKubernetesClientResourceTest`: an inline ArgoCD `Application` fixture with the same
`apiVersion`/`kind`/`spec.destination.namespace` shape but `spec.source.chart:
some-unrelated-workload-chart` produces exactly the two ordinary callbacks and nothing more. The
existing positive test (`deployCR_argoCdApplication_alsoReportsAdditionalSecretResourceForDestinationNamespace`,
using the `provision-k8s-cluster-argocd-tf-controller` fixture) still passes unchanged, confirming the
chart-matching case is unaffected.

Full suite: `mvn test` → 21/21 green (`CridgeIntegrationTest` 6, `PlanningCallbackClientTest` 5,
`SimulatedKubernetesClientResourceTest` 10 — up from 9).

## Related

Builds on [20260812_1830_argocd-application-secret-simulation.md](20260812_1830_argocd-application-secret-simulation.md)
and [20260814_1230_simulated-secret-resource-characteristics.md](20260814_1230_simulated-secret-resource-characteristics.md).
Loading