Commit 36a623a3 authored by Konstantinos Togias's avatar Konstantinos Togias
Browse files

test: add isolated OEG SDK acceptance checkpoints

parent f5901b6a
Loading
Loading
Loading
Loading
+12 −0
Changes for .gitlab-ci.yml: 12 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -65,3 +65,15 @@ python-smoke:
  stage: test
  script:
    - python3 -m py_compile ansible/roles/oop-platform/files/fm_partners.py ansible/tests/r2_e2e/*.py

# Pure gate/observation checks; no inventory, cluster access or deployment secrets.
sdk-acceptance-checks:
  stage: test
  variables:
    TMPDIR: "$CI_PROJECT_DIR/.cache/sdk-tmp"
    PYTHONDONTWRITEBYTECODE: "1"
  script:
    - mkdir -p "$TMPDIR"
    - pip install pytest==8.3.2 requests==2.32.5
    - python -m pytest --confcutdir=ansible/tests/unit -q -p no:cacheprovider ansible/tests/unit/test_sdk_acceptance.py
    - python -m pytest --collect-only -q -p no:cacheprovider ansible/tests/r2_e2e
+65 −0
Changes for ansible/inventory/r2-sdk-lab.example.yml: 65 added lines, 0 removed lines.
Original line number Diff line number Diff line
---
# Separate, single-operator lab. Copy to r2-sdk-lab.local.yml and fill the pins.
# No production inventory, federation, shared workload namespace or floating SDK install.
all:
  children:
    oop_platform_operators:
      hosts:
        sdk_lab:
          ansible_connection: local
          ansible_python_interpreter: "{{ ansible_playbook_python }}"
          oop_platform_kubeconfig: ~/.kube/oop-sdk-lab.yaml
          oop_platform_kube_context: oop-sdk-lab
          oop_platform_namespace: oop-e2e-platform
          oop_platform_public_host: sdk-lab.example.test
          oop_platform_federation_id: sdk-lab
          oop_platform_country_code: GR
          oop_platform_mcc: "202"
          oop_platform_mncs: ["01"]
          oop_platform_federation_clients: []
          oop_platform_portal_enabled: false
          oop_platform_chart_version: 36dda75882352216ced21475786160ee2e669a6b
          oop_platform_fm_image_tag: REPLACE_WITH_REVIEWED_FM_TAG
          oop_platform_oeg_image_tag: REPLACE_WITH_REVIEWED_OEG_TAG
          oop_platform_srm_image_tag: REPLACE_WITH_REVIEWED_SRM_TAG
          e2e_temp_dir: "{{ playbook_dir }}/../../../.cache/sdk-e2e-tmp"
          oop_platform_extra_values:
            srm:
              srmcontroller:
                workloads:
                  namespaces: [oop-e2e-workloads]
                controlPaths:
                  - control_path_ref: sdk-lab-k8s
                    adapter: kubernetes
                    config:
                      namespace: oop-e2e-workloads
                topology: |
                  version: openop.srm.bootstrap/v1
                  platform_instance_id: sdk-lab
                  control_paths:
                    - ref: sdk-lab-k8s
                      adapter_family: compute
                      adapter_type: kubernetes
                      adapter_contract_version: v1
                      infrastructure:
                        namespace: oop-e2e-workloads
                  zones:
                    - ref: sdk-lab-zone
                      name: SDK lab
                      kind: resource
                      state: active
                      domains:
                        - ref: sdk-lab-compute
                          name: SDK lab compute
                          kind: compute
                          state: active
                          capabilities:
                            - ref: sdk-lab-workload
                              name: SDK lab workload
                              kind: workload
                              state: active
                              control_path_bindings:
                                - ref: sdk-lab-workload-binding
                                  control_path_ref: sdk-lab-k8s
                                  is_default: true
                                  state: active
+11 −0
Changes for ansible/playbooks/scenarios/r2_existing_cluster/e2e.yml: 11 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -12,6 +12,15 @@
  hosts: oop_platform_operators
  gather_facts: true
  tasks:
    - name: Prepare an explicitly configured controller temporary directory
      ansible.builtin.file:
        path: "{{ e2e_temp_dir }}"
        state: directory
        mode: "0700"
      when: e2e_temp_dir is defined
      delegate_to: localhost
      run_once: true

    - name: Export the settings
      ansible.builtin.include_role:
        name: oop-platform
@@ -33,6 +42,7 @@

        - name: Make a file for it on the controller
          ansible.builtin.tempfile:
            path: "{{ e2e_temp_dir | default(omit) }}"
            suffix: .kubeconfig
          register: e2e_kubeconfig_file
          delegate_to: localhost
@@ -65,6 +75,7 @@
  tasks:
    - name: Write the settings for the tests
      ansible.builtin.tempfile:
        path: "{{ e2e_temp_dir | default(omit) }}"
        suffix: .json
      register: e2e_settings_file

+96 −0
Changes for ansible/tests/r2_e2e/SDK_ACCEPTANCE.md: 96 added lines, 0 removed lines.
Original line number Diff line number Diff line
# OEG deployment-to-TI acceptance (TI-E2E-01)

Run this scenario in a separate single-operator lab. Default `e2e.yml` runs keep
the existing tests; the two `sdk_acceptance` cases are deselected. Explicit SDK
selection runs only the requested checkpoint, leaving federation/auth tests out
of this run. No existing test is marked xfail or weakened.

## Prepare the lab

1. Copy `inventory/r2-sdk-lab.example.yml` to `inventory/r2-sdk-lab.local.yml`.
   Supply a dedicated cluster/context and published component image tags. The
   reviewed chart is pinned. This example deliberately contains placeholders.
2. Deploy that inventory with `r2_existing_cluster/deploy.yml`. Do not federate
   it or reuse another test suite's workload namespace. Configure DNS/routing so
   the advertised workload host is reachable from the test controller.
3. Build SRM with the reviewed TF-SDK wheel installed during image build. Pin
   its dependencies and record the image digest; do not pip-install over a
   running/shared SRM. Include the required Helm client and backend credentials.
4. Copy `profiles/deploy_to_ti.example.json` into an ignored `.cache/` directory
   and fill real image digests, deployment/container names and lab identities.
   Use a digest-pinned [traefik/whoami](https://github.com/traefik/whoami) fixture:
   it listens on port 80 and returns its hostname, allowing the test to prove
   that HTTP reached this run's pod rather than a different HTTP 200 endpoint.
   Compute `sdk.sha256` from the independently reviewed wheel:

   ```sh
   python tests/r2_e2e/sdk_artifact.py /disk/artifacts/openop_tfsdk-1.1.1-py3-none-any.whl
   ```

   The same script runs read-only in the selected SRM pod. Python/chart contents
   and package version must match; version 1.1.1 alone cannot identify a commit.
   Record the reviewed source SHA alongside the wheel fingerprint. The test
   verifies both SRM/OEG running image digests before creating a test client.

Use disk-backed `TMPDIR` and `ANSIBLE_LOCAL_TEMP`. The lab inventory sets
`e2e_temp_dir` for controller settings/kubeconfig copies; no /tmp is required.
Keep a JUnit report and the generated `.cache/sdk-e2e/<run-id>/run.json`.

## Run the first checkpoint

From `ansible/`, using this lab only:

```sh
ansible-playbook -i inventory/r2-sdk-lab.local.yml \
  playbooks/scenarios/r2_existing_cluster/e2e.yml \
  -e '{"e2e_pytest_args":"--sdk-checkpoint=endpoints --sdk-profile=.cache/sdk-profile.json --junitxml=.cache/sdk-endpoints.xml"}'
```

The endpoint case registers one digest-pinned HTTP identity container, deploys it through
OEG and polls the public filtered instance read until ready. It requires an
`http` endpoint, checks its NodePort against the live Service, checks the same
deployment's image and makes a request from the controller to the returned host
and port. The response must identify the selected ready pod. It does not inject
endpoints or query/write platform databases.

Before the run, the workload namespace must contain no Service/Deployment. The
scenario uses a fresh application ID and ephemeral scoped Keycloak client.
Cleanup uses public OEG deletion, only for this run's application/instances/TI,
then removes that client. Cleanup failures are reported alongside the test
failure, and no namespace, platform release or unrelated resource is deleted.

The current SDK baseline returns no endpoints, so this assertion is expected
to fail once a compatible lab is available. That is not a live failure already
reproduced by this change. Capture the red baseline before changing the SDK.

## Complete the TI checkpoint separately

Select `--sdk-checkpoint=ti` only after the NEF owner agrees and enables the
application-wide profile and supplies its subscriptions URL and actual DNAI.
Configure the network control path in the lab; the example inventory covers
only workload. Never fabricate a device default to satisfy this public request.
Set `ti.token_env` to an environment variable name if NEF reads need a bearer
token; no token belongs in the profile or report.

The TI case repeats the real deployment/endpoint checks, creates public TI by
appId/appInstanceId and destination TCP/port, waits for public `active`, then
reads the corresponding NEF policy. This bounded profile requires `anyUeInd`,
no individual-user selector, one TCP filter to the observed IPv4/port, and the
agreed DNAI. It refuses a broader IP-only flow. This is a control-plane test;
packet forwarding and notification delivery require separate acceptance.

The current app-wide/provider-mode, SRM enrichment and SDK filter gaps can make
TI fail independently of SDK endpoint output. Keep those failures owned by
their modules rather than changing the SDK to perform SRM inventory selection.

## Fast verification without a lab

```sh
python -m pytest tests/unit/test_sdk_acceptance.py
python -m pytest --collect-only tests/r2_e2e
```

The first checks the new gate/observation helpers with controlled inputs. The
second must retain the same 23 default cases and deselect the two SDK cases.
Neither is evidence of a live OEG/SRM/SDK/backend run. No live test is added to
the existing CI jobs by this change.
+38 −0
Changes for ansible/tests/r2_e2e/conftest.py: 38 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -17,6 +17,44 @@ from pathlib import Path
import pytest
import requests


def pytest_addoption(parser):
    group = parser.getgroup("SDK acceptance")
    group.addoption("--sdk-checkpoint", choices=("endpoints", "ti"), default=None)
    group.addoption("--sdk-profile", help="JSON profile for a dedicated SDK acceptance lab")


def pytest_configure(config):
    config.addinivalue_line("markers", "sdk_acceptance(checkpoint): opt-in real SDK lab scenario")
    if config.getoption("sdk_checkpoint"):
        from sdk_scenario import load_profile

        if not config.getoption("sdk_profile"):
            raise pytest.UsageError("--sdk-checkpoint requires --sdk-profile")
        try:
            config.sdk_profile = load_profile(
                config.getoption("sdk_profile"), config.getoption("sdk_checkpoint")
            )
        except (OSError, ValueError, KeyError, TypeError) as error:
            raise pytest.UsageError(f"Invalid SDK acceptance profile: {error}") from error
    elif config.getoption("sdk_profile"):
        raise pytest.UsageError("--sdk-profile requires --sdk-checkpoint")


def pytest_collection_modifyitems(config, items):
    checkpoint = config.getoption("sdk_checkpoint")
    selected, deselected = [], []
    for item in items:
        mark = item.get_closest_marker("sdk_acceptance")
        keep = (mark is None) if checkpoint is None else (
            mark is not None and mark.args == (checkpoint,)
        )
        (selected if keep else deselected).append(item)
    if deselected:
        config.hook.pytest_deselected(items=deselected)
        items[:] = selected


TIMEOUT = 30
EWBI = "/operatorplatform/federation/v1"
FM_CALL = """
Loading