Commit f8e5dc6b authored by Sergio Gimenez's avatar Sergio Gimenez
Browse files

Merge branch 'feature/lite2edge-integration' into 'main'

Feature/lite2edge integration

See merge request !8
parents a62d62f1 b206f430
Loading
Loading
Loading
Loading
Loading

AGENTS.md

0 → 100644
+297 −0
Original line number Original line Diff line number Diff line
# Agent Guidelines for Federation Manager

This document provides coding agents with essential information for working in the Federation Manager codebase.

## Project Overview

**Federation Manager** is a Python-based component implementing Federation Management functionality for the GSMA Operator Platform (OP). It supports dual-role architecture:
- **Partner OP Mode**: Handles external federation partner requests (without `X-Internal` header)
- **Originating OP Mode**: Handles internal service requests (with `X-Internal` and `X-Partner-API-Root` headers)

## Build/Run Commands

### Setup
```bash
# Create virtual environment
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install dependencies
pip install -r requirements.txt

# Configure application
cp src/conf/config.cfg.sample src/conf/config.cfg
# Edit config.cfg with your specific settings
```

### Run Application
```bash
# Local development
cd src/
python main.py

# Using Docker Compose (recommended)
docker compose up -d

# Build Docker image
docker build -t federation-manager .
```

### Testing

**Run all tests:**
```bash
cd src/
python test/run_all_tests.py --verbose --coverage
```

**Run specific test module:**
```bash
cd src/
python -m unittest test.test_federation_management -v
```

**Run single test case:**
```bash
cd src/
python -m unittest test.test_federation_management.TestFederationManagementController.test_01_create_federation -v
```

**Test execution order** (tests run sequentially):
1. `test_federation_management`
2. `test_availability_zone_info_synchronization`
3. `test_artefact_management`
4. `test_application_onboarding_management`
5. `test_application_deployment_management`

**Coverage reports:**
- Terminal output with coverage percentage
- HTML report in `src/htmlcov/` directory

### Linting
```bash
# No formal linting configured, but PEP8 style is followed
# Use flake8 or pylint manually if needed
flake8 src/ --max-line-length=120
```

## Architecture

```
API Layer (src/api/) 

Adapter Layer (src/adapters/)
  ├── tf_adapter/ (Partner OP mode - external requests)
  └── fm_adapter/ (Originating OP mode - internal requests)

Client Layer (src/clients/)
  ├── fed_manager.py (Federation Manager client)
  └── tf_sdk.py (Edge Cloud Platform SDK)
```

**Request routing** via `adapters.injector.resolve_adapter()`:
- Without `X-Internal` header → `tf_adapter` (Partner OP)
- With `X-Internal` header → `fm_adapter` (Originating OP)

## Code Style Guidelines

### General Python Standards
- Follow **PEP8** coding conventions
- Python **3.12+** required
- Use **snake_case** for functions and variables
- Use **PascalCase** for class names
- Maximum line length: **120 characters** (flexible)

### Imports
```python
# Standard library imports (absolute)
from __future__ import absolute_import
import os
import sys
from configparser import ConfigParser
from datetime import date, datetime  # noqa: F401

# Third-party imports
import connexion
from flask import abort, render_template
from flask_mongoengine import MongoEngine

# Local application imports
from adapters.injector import resolve_adapter
from adapters.error import APIError
from models.federation_request_data import FederationRequestData  # noqa: E501
import util
```

**Import ordering:**
1. `from __future__` imports (if needed for compatibility)
2. Standard library imports
3. Third-party library imports
4. Local application imports

**Import conventions:**
- Use explicit imports from packages
- Long model imports are acceptable with `# noqa: E501` to suppress line length warnings
- Use `# noqa: F401` for imports needed only for type hints

### File Headers
All Python files must include the Apache 2.0 license header:
```python
# -------------------------------------------------------------------------- #
# Copyright 2025-present, Federation Manager, by Software Networks, i2CAT    #
#                                                                            #
# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
# not use this file except in compliance with the License. You may obtain    #
# a copy of the License at                                                   #
#                                                                            #
# http://www.apache.org/licenses/LICENSE-2.0                                 #
#                                                                            #
# Unless required by applicable law or agreed to in writing, software        #
# distributed under the License is distributed on an "AS IS" BASIS,          #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
# See the License for the specific language governing permissions and        #
# limitations under the License.                                             #
# -------------------------------------------------------------------------- #
```

### Type Hints
```python
# Function signatures with type hints
def create_federation(body: FederationRequestData) -> FederationResponseData:
    """Creates one direction federation with partner operator platform."""
    pass

# Model classes use swagger_types for type information
class CountryCode(Model):
    def __init__(self):
        self.swagger_types = {}
        self.attribute_map = {}
```

### Docstrings
```python
def create_federation(body):  # noqa: E501
    """Creates one direction federation with partner operator platform.

     # noqa: E501

    :param body:
    :type body: dict | bytes

    :rtype: FederationResponseData
    """
    pass
```

### Configuration Management
```python
# Use ConfigParser for configuration
from configparser import ConfigParser

CONFIG = ConfigParser()
config_file = os.environ.get("FM_CONFIG_FILE", "conf/config.cfg")
CONFIG.read(config_file)
HOST = CONFIG.get("server", "host")
PORT = int(CONFIG.get("server", "port"))
```

### Error Handling
```python
# Use custom APIError for API errors
from adapters.error import APIError

try:
    adapter = resolve_adapter(headers)
    return adapter.federation_management.create_federation(body, bearer_token, partner_api_root)
except APIError as error:
    abort(error.status_code, error.detail_error)

# Use abort() for HTTP errors
from flask import abort

try:
    body = FederationRequestData.from_dict(connexion.request.get_json())
except Exception as error:
    abort(422, f"Federation Validation Error. Message: {error}")
```

### Testing Conventions
```python
# Test classes inherit from BaseTestCase
from test import BaseTestCase

class TestFederationManagementController(BaseTestCase):
    """FederationManagementController integration test stubs"""
    
    BaseTestCase.federation_context_id_partner = ""
    BaseTestCase.token = ""
    
    def test_01_create_federation(self):
        """Test case for create_federation in both Partner OP and Originating OP modes"""
        # Test both roles
        pass
```

**Dual role testing:**
- All integration tests must validate both Partner OP and Originating OP modes
- Use `make_request_partner_op()` for external requests (no `X-Internal` header)
- Use `make_request_originating_op()` for internal requests (with `X-Internal` header)

### Naming Conventions
- **Functions/Methods**: `snake_case` (e.g., `create_federation`, `get_federation_details`)
- **Classes**: `PascalCase` (e.g., `FederationRequestData`, `BaseTestCase`)
- **Constants**: `UPPER_CASE` (e.g., `CONFIG`, `HOST`, `PORT`)
- **Variables**: `snake_case` (e.g., `bearer_token`, `partner_api_root`)
- **Test methods**: Prefix with `test_` and optionally number for execution order (e.g., `test_01_create_federation`)

## Key Files and Locations

- **Application entry**: `src/main.py`
- **API endpoints**: `src/api/`
- **Business logic**: `src/adapters/fm_adapter/` and `src/adapters/tf_adapter/`
- **Client SDKs**: `src/clients/`
- **Data models**: `src/models/`
- **Tests**: `src/test/`
- **Configuration**: `src/conf/config.cfg`
- **OpenAPI spec**: `src/swagger/swagger.yaml`

## Development Workflow

1. **Before making changes**: Review the dual-role architecture to understand request routing
2. **Configuration**: Always use `ConfigParser` to read from `conf/config.cfg`
3. **Adding endpoints**: Update `src/swagger/swagger.yaml` first, then implement in `src/api/`
4. **Adding business logic**: Implement in both adapters if applicable (fm_adapter and tf_adapter)
5. **Testing**: Write integration tests that validate both Partner OP and Originating OP modes
6. **Documentation**: Update docstrings and inline comments; avoid creating unnecessary markdown files

## Common Patterns

### Request Header Extraction
```python
bearer_token = util.get_token_from_request(connexion)
headers = dict(connexion.request.headers)
partner_api_root = headers.get("X-Partner-Api-Root")
```

### Adapter Resolution
```python
from adapters.injector import resolve_adapter

adapter = resolve_adapter(headers)  # Returns tf_adapter or fm_adapter based on X-Internal header
return adapter.federation_management.create_federation(body, bearer_token, partner_api_root)
```

### Model Deserialization
```python
body = FederationRequestData.from_dict(connexion.request.get_json())
```

## Notes for AI Agents

- **Never skip the Apache 2.0 license header** when creating new files
- **Always test both dual roles** when modifying API or adapter code
- **Configuration changes** should be documented and may require updates to `config.cfg.sample`
- **Docker builds** use `gunicorn` as the WSGI server (see `Dockerfile`)
- **Authentication** via OAuth 2.0 with Keycloak integration
- **Database**: MongoDB with MongoEngine ORM
- **API Framework**: Connexion (Flask-based) with OpenAPI/Swagger specifications
+11 −21
Original line number Original line Diff line number Diff line
@@ -22,11 +22,7 @@
#########################################################
#########################################################


FROM python:3.12
FROM python:3.12

# Set working directory
WORKDIR /usr/app
WORKDIR /usr/app

# Install system dependencies
RUN apt-get update && apt-get install -y \
RUN apt-get update && apt-get install -y \
  bash \
  bash \
  build-essential \
  build-essential \
@@ -36,18 +32,12 @@ RUN apt-get update && apt-get install -y \
  libcurl4-openssl-dev \
  libcurl4-openssl-dev \
  libssl-dev \
  libssl-dev \
  && rm -rf /var/lib/apt/lists/*
  && rm -rf /var/lib/apt/lists/*

# Copy application code
# Copy application code
COPY . . 
COPY . . 

ARG PIP_INDEX_URL
# Install Python dependencies
# Install Python dependencies
RUN python -m pip install --no-cache-dir -r requirements.txt
RUN python -m pip install --no-cache-dir -r requirements.txt --trusted-host gitlab.i2cat.net --extra-index-url ${PIP_INDEX_URL}

# Set working directory for application execution
WORKDIR /usr/app/src/
WORKDIR /usr/app/src/

# Expose application port
EXPOSE 8989
EXPOSE 8989

# Set Gunicorn as the entrypoint
# Set Gunicorn as the entrypoint
ENTRYPOINT ["gunicorn", "wsgi:app", "--bind", "0.0.0.0:8989", "--workers", "4", "--log-level", "debug", "--timeout", "1000"]
ENTRYPOINT ["gunicorn", "wsgi:app", "--bind", "0.0.0.0:8989", "--workers", "4", "--log-level", "debug", "--timeout", "1000"]

Dockerfile.dev

0 → 100644
+57 −0
Original line number Original line Diff line number Diff line
# -------------------------------------------------------------------------- #
# Copyright 2025-present, Federation Manager, by Software Networks, i2CAT    #
#                                                                            #
# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
# not use this file except in compliance with the License. You may obtain    #
# a copy of the License at                                                   #
#                                                                            #
# http://www.apache.org/licenses/LICENSE-2.0                                 #
#                                                                            #
# Unless required by applicable law or agreed to in writing, software        #
# distributed under the License is distributed on an "AS IS" BASIS,          #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
# See the License for the specific language governing permissions and        #
# limitations under the License.                                             #
# -------------------------------------------------------------------------- #

#########################################################
#                                                       #
#   Dev Dockerfile for creating a container image      #
#   for the federation-manager with local TF-SDK       #
#                                                       #
#########################################################

FROM python:3.12
WORKDIR /usr/app
RUN apt-get update && apt-get install -y \
    bash \
    build-essential \
    git \
    wget \
    iptables \
    libcurl4-openssl-dev \
    libssl-dev \
    && rm -rf /var/lib/apt/lists/*

# Copy local TF-SDK first
COPY ../tf-sdk /tmp/tf-sdk

# Copy application code
COPY . . 

ARG PIP_INDEX_URL
ARG PIP_EXTRA_INDEX_URL

# Install local TF-SDK instead of the published version
RUN pip install --no-cache-dir /tmp/tf-sdk

# Install remaining Python dependencies (TF-SDK will be skipped since already installed)
RUN python -m pip install --no-cache-dir -r requirements.txt --trusted-host gitlab.i2cat.net --extra-index-url ${PIP_EXTRA_INDEX_URL}

# Clean up
RUN rm -rf /tmp/tf-sdk

WORKDIR /usr/app/src/
EXPOSE 8989
# Set Gunicorn as the entrypoint
ENTRYPOINT ["gunicorn", "wsgi:app", "--bind", "0.0.0.0:8989", "--workers", "4", "--log-level", "debug", "--timeout", "1000"]
+12 −0
Original line number Original line Diff line number Diff line
@@ -21,6 +21,18 @@
      "serviceAccountsEnabled": true,
      "serviceAccountsEnabled": true,
      "defaultClientScopes": ["fed-mgmt"],
      "defaultClientScopes": ["fed-mgmt"],
      "webOrigins": ["*"]
      "webOrigins": ["*"]
    },
    {
      "clientId": "originating-op-2",
      "enabled": true,
      "clientAuthenticatorType": "client-secret",
      "secret": "2mhznERfWclLDuVojY77Lp4Qd2r4e8Ms",
      "redirectUris": ["http://localhost:8080/*"],
      "publicClient": false,
      "directAccessGrantsEnabled": true,
      "serviceAccountsEnabled": true,
      "defaultClientScopes": ["fed-mgmt"],
      "webOrigins": ["*"]
    }
    }
  ]
  ]
}
}
+3 −3
Original line number Original line Diff line number Diff line
attrs==23.1.0
attrs==23.1.0
Authlib==1.2.1
Authlib==1.2.1
certifi==2023.11.17
certifi==2023.11.17
cffi==1.16.0
cffi==2.0.0
charset-normalizer==3.3.2
charset-normalizer==3.3.2
click==8.1.7
click==8.1.7
clickclick==20.10.2
clickclick==20.10.2
@@ -27,7 +27,7 @@ MarkupSafe==2.1.3
mongoengine==0.29.1
mongoengine==0.29.1
packaging==23.2
packaging==23.2
pkgutil-resolve-name==1.3.10
pkgutil-resolve-name==1.3.10
pycparser==2.21
pycparser==2.23
PyJWT==2.8.0
PyJWT==2.8.0
pymongo==4.13.2
pymongo==4.13.2
python-dateutil==2.9.0
python-dateutil==2.9.0
@@ -36,7 +36,7 @@ referencing==0.32.0
requests==2.32.4
requests==2.32.4
rpds-py==0.13.2
rpds-py==0.13.2
six==1.16.0
six==1.16.0
sunrise6g-opensdk==1.0.8
sunrise6g-opensdk==1.0.29
swagger-ui-bundle==0.0.9
swagger-ui-bundle==0.0.9
urllib3==2.1.0
urllib3==2.1.0
Werkzeug==2.2.3
Werkzeug==2.2.3
Loading