From b0282eaee78e61dc4e43323df778a862185634fb Mon Sep 17 00:00:00 2001 From: Mohammed Hassan Date: Mon, 12 Jan 2026 23:13:40 +0100 Subject: [PATCH 1/3] update readme --- bandit-report.json | 132 -------------------------------------------- challenge/README.md | 126 ------------------------------------------ 2 files changed, 258 deletions(-) delete mode 100644 bandit-report.json delete mode 100644 challenge/README.md diff --git a/bandit-report.json b/bandit-report.json deleted file mode 100644 index 30d0e58..0000000 --- a/bandit-report.json +++ /dev/null @@ -1,132 +0,0 @@ -{ - "errors": [], - "generated_at": "2026-01-12T19:59:34Z", - "metrics": { - "_totals": { - "CONFIDENCE.HIGH": 0, - "CONFIDENCE.LOW": 0, - "CONFIDENCE.MEDIUM": 1, - "CONFIDENCE.UNDEFINED": 0, - "SEVERITY.HIGH": 0, - "SEVERITY.LOW": 0, - "SEVERITY.MEDIUM": 1, - "SEVERITY.UNDEFINED": 0, - "loc": 935, - "nosec": 0, - "skipped_tests": 0 - }, - "service/__init__.py": { - "CONFIDENCE.HIGH": 0, - "CONFIDENCE.LOW": 0, - "CONFIDENCE.MEDIUM": 0, - "CONFIDENCE.UNDEFINED": 0, - "SEVERITY.HIGH": 0, - "SEVERITY.LOW": 0, - "SEVERITY.MEDIUM": 0, - "SEVERITY.UNDEFINED": 0, - "loc": 0, - "nosec": 0, - "skipped_tests": 0 - }, - "service/config.py": { - "CONFIDENCE.HIGH": 0, - "CONFIDENCE.LOW": 0, - "CONFIDENCE.MEDIUM": 1, - "CONFIDENCE.UNDEFINED": 0, - "SEVERITY.HIGH": 0, - "SEVERITY.LOW": 0, - "SEVERITY.MEDIUM": 1, - "SEVERITY.UNDEFINED": 0, - "loc": 16, - "nosec": 0, - "skipped_tests": 0 - }, - "service/lib/__init__.py": { - "CONFIDENCE.HIGH": 0, - "CONFIDENCE.LOW": 0, - "CONFIDENCE.MEDIUM": 0, - "CONFIDENCE.UNDEFINED": 0, - "SEVERITY.HIGH": 0, - "SEVERITY.LOW": 0, - "SEVERITY.MEDIUM": 0, - "SEVERITY.UNDEFINED": 0, - "loc": 22, - "nosec": 0, - "skipped_tests": 0 - }, - "service/lib/data.py": { - "CONFIDENCE.HIGH": 0, - "CONFIDENCE.LOW": 0, - "CONFIDENCE.MEDIUM": 0, - "CONFIDENCE.UNDEFINED": 0, - "SEVERITY.HIGH": 0, - "SEVERITY.LOW": 0, - "SEVERITY.MEDIUM": 0, - "SEVERITY.UNDEFINED": 0, - "loc": 189, - "nosec": 0, - "skipped_tests": 0 - }, - "service/lib/models.py": { - "CONFIDENCE.HIGH": 0, - "CONFIDENCE.LOW": 0, - "CONFIDENCE.MEDIUM": 0, - "CONFIDENCE.UNDEFINED": 0, - "SEVERITY.HIGH": 0, - "SEVERITY.LOW": 0, - "SEVERITY.MEDIUM": 0, - "SEVERITY.UNDEFINED": 0, - "loc": 101, - "nosec": 0, - "skipped_tests": 0 - }, - "service/lib/radix_tree.py": { - "CONFIDENCE.HIGH": 0, - "CONFIDENCE.LOW": 0, - "CONFIDENCE.MEDIUM": 0, - "CONFIDENCE.UNDEFINED": 0, - "SEVERITY.HIGH": 0, - "SEVERITY.LOW": 0, - "SEVERITY.MEDIUM": 0, - "SEVERITY.UNDEFINED": 0, - "loc": 180, - "nosec": 0, - "skipped_tests": 0 - }, - "service/main.py": { - "CONFIDENCE.HIGH": 0, - "CONFIDENCE.LOW": 0, - "CONFIDENCE.MEDIUM": 0, - "CONFIDENCE.UNDEFINED": 0, - "SEVERITY.HIGH": 0, - "SEVERITY.LOW": 0, - "SEVERITY.MEDIUM": 0, - "SEVERITY.UNDEFINED": 0, - "loc": 427, - "nosec": 0, - "skipped_tests": 0 - } - }, - "results": [ - { - "code": "9 def __init__(self):\n10 self.host: str = os.getenv(\"HOST\", \"0.0.0.0\")\n11 self.port: int = int(os.getenv(\"PORT\", \"5000\"))\n", - "col_offset": 43, - "end_col_offset": 52, - "filename": "service/config.py", - "issue_confidence": "MEDIUM", - "issue_cwe": { - "id": 605, - "link": "https://cwe.mitre.org/data/definitions/605.html" - }, - "issue_severity": "MEDIUM", - "issue_text": "Possible binding to all interfaces.", - "line_number": 10, - "line_range": [ - 10 - ], - "more_info": "https://bandit.readthedocs.io/en/1.9.2/plugins/b104_hardcoded_bind_all_interfaces.html", - "test_id": "B104", - "test_name": "hardcoded_bind_all_interfaces" - } - ] -} \ No newline at end of file diff --git a/challenge/README.md b/challenge/README.md deleted file mode 100644 index ddbb52d..0000000 --- a/challenge/README.md +++ /dev/null @@ -1,126 +0,0 @@ -# Set-Up Instructions - -We provide you two Docker containers, combined via docker-compose. While you can decide on your own what kind of -environment you want to use for development, please make sure that the final result will work in the provided -containers. It is accepted to change the Dockerfiles and/or docker-compose file if necessary for your solution. - -Docker and docker-compose needs to be installed on your machine. To build and run the container network as a whole you -can use docker-compose directly: - - docker-compose up --build - -If you want to build the containers separately, you can use `docker build` to do so: - - docker build -f Dockerfile-testrunner -t sony-nre-testwork-testrunner . - docker build -f Dockerfile-service -t sony-nre-testwork-testservice . - -The `service` container, described in `Dockerfile-service` is where we want you to implement your service for. - -The `testrunner` container is the counterpart, supposed to test your solution. You are allowed to change this container -(and it's content), but it is not required or mandatory to do so. - -# Task Overview - -We are deploying a REST based network service, which is helping our organization to perform routing table lookups. - -The tests provided within the `testrunner`-container should be able to run successful against the service build by you -within this task. - -The service is fed by a full internet routing table (today, this consists of roughly one million routes). We are -providing you a "real" routing table along with this assignment you must use as your database (see `routes.txt`). -The format of the file is simply - - ; - -The REST service we want to deploy should expose an endpoint at - - /destination/ - -For instance, the HTTP request - - GET /destination/192.168.0.1 - -should perform a routing lookup for `192.168.0.1` and return the result in the following format - - {"dst": "", "nh": ""} - -If no route is found, the service shall return a HTTP 404 error. - -The routing table look-up is supposed to implement the actual algorithm used to perform routing-table look-ups as -common operating systems implement it today (refer to [RFC 1812](https://www.ietf.org/rfc/rfc1812.txt) in case you -need help understanding how routers perform a routing table look-up). - -Moreover, the service should support policy-based routing decisions, thus a metric value should be considered -accordingly when looking for the best route. - -If entries have both, matching destination prefix and same metric, the best route should be selected based on the -lowest next-hop IP address in integer representation as a tie-breaker. - -The service should support update a route's metric by sending a `PUT` request in the following format - - /prefix//nh//metric//match/ - /prefix//nh//metric// - -The classifier can be either `exact` or `orlonger`. When omitted, `orlonger` should be considered the default. - -- If the classifier is set to `exact` only exact prefix matches should get the metric applied to. -- If the classifier is set to `orlonger` all prefixes, including all subnet-of prefixes would get the metric applied. -- next-hop should be always considered when updating a route entry - -The metric is an integer in the range [1, 32768]. - -As it is common in the router world, the metric with the lowest value is preferred over metrics with a higher value. -The metric must be taken into consideration for any subsequent `GET /destination` table look-up thereafter. - -For instance, the following HTTP request - - PUT /prefix/10.0.0.0%2F16/nh/192.168.1.100/metric/100 - -would apply the metric `100` to all matching routes within `10.0.0.0/16` that have `192.168.1.100` as next-hop. - -The request should return the HTTP status code 200 if at least one route was updated, or 404 if no routes were updated. - -# Testing Your Assignment - -We are shipping a test client container you can use to test your service against. It is supposed to give you some -assistance to help bringing your service up. The tests we are shipping to you are **non-conclusive**. We will -perform more tests, than those we've sent to you, with more corner cases that we did not include in our copy handed -over to you. - -## Running the Tests - -The tests are implemented using `py.test`. It is recommended to simply run them by starting the docker compose-network. - -If you want to start the tests manually, either just start the `testrunnner`-container or execute pytest locally from -your working directory. You might have to adjust the hostname used within the tests if you want to use the testrunner -locally. - -# Submitting Your Assignment - -Please submit your assignment as a self-sufficient compressed archive back to us. You may edit the `Dockerfile(s)` -as needed, to include everything allowing your service to run. You may add dependencies, scripts and whatever else you -need. The service you're submitting must not rely on an available internet connection at runtime. - -# What We Will Be Looking At - -You are generally free to implement the service in any way, we don't put restrictions on frameworks, libraries or -dependencies on you (but please submit Python 3 code). We will run and validate your code as you submit it. Roughly, -we will: - -- look if you solved the problem in a correct way -- look what your approach to the problem was, and what the algorithmic complexity of it is -- how the quality of the code is you submit to us -- what the patterns and practices are you use - -Try to keep the code quality on a level that you feel confident about, that you are familiar with and that you would -expect to see in production code. - -We think, you should be able to clock-in somewhere around 8 hours to solve this problem. Take the time you need, -we don't value speed over quality. However, we might ask you for an estimate of how long it took you to write your -solution and which parts took you the longest and why you think that was the case. - -You are allowed to change requirements, tests or anything else about this testtask. If you do so, please write down -the reasoning behind your change. In general, no writeup describing your solution or the process of solving this task -is required. Still, if you make interesting decisions or have anything else you want us to know/consider when -evaluating your solution, feel free to write as much as you like. We certainly enjoy getting an insight of your -approach and mindset while solving this task. From be79a9c888568eb9f681461246c794e40efa5a0c Mon Sep 17 00:00:00 2001 From: Mohammed Hassan Date: Mon, 12 Jan 2026 23:16:19 +0100 Subject: [PATCH 2/3] update readme --- CONTRIBUTING.md | 308 +++++++++++++++++++++++++++++++++++++++++++++++- README.md | 109 +++++++++-------- 2 files changed, 369 insertions(+), 48 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 82596b0..53c9fee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,2 +1,306 @@ -# Contribution -TODO: move contribution from readme to here +# Contributing to Routing Table API + +Contributions are welcome! Please follow these guidelines to ensure high quality code and smooth collaboration. + +--- + +## ๐Ÿš€ Quick Start + +```bash +# Fork and clone +git clone https://github.com/yourusername/routing-table-api.git +cd routing-table-api +make install + +# Create feature branch +git checkout -b feature/amazing-feature + +# Make changes and test +make test-cov # Must maintain โ‰ฅ35% coverage +make lint # Must pass +make type-check # Must pass + +# Commit using conventional commits +git commit -m "feat: add amazing feature" +``` + +--- + +## โœ… Requirements + +All contributions must meet these criteria: + +- โœ… **All tests pass** (29/29) +- โœ… **Coverage maintained** (โ‰ฅ35%) +- โœ… **Linter passes** (`make lint`) +- โœ… **Type hints** for new code +- โœ… **Google-style docstrings** +- โœ… **PEP 8 compliance** (enforced by ruff) + +--- + +## ๐Ÿ“ Commit Convention + +Use [Conventional Commits](https://www.conventionalcommits.org/) format: + +| Type | Use Case | +|------|----------| +| `feat:` | New feature | +| `fix:` | Bug fix | +| `docs:` | Documentation only | +| `refactor:` | Code refactoring | +| `test:` | Adding/updating tests | +| `chore:` | Build/tooling changes | +| `perf:` | Performance improvement | + +**Examples:** +```bash +git commit -m "feat: add IPv6 support to radix tree" +git commit -m "fix: handle edge case in LPM lookup" +git commit -m "docs: add Kubernetes deployment guide" +git commit -m "test: add concurrency tests for thread safety" +``` + +--- + +## ๐Ÿงช Testing + +### Run All Tests + +```bash +make test # Run all 29 tests +make test-cov # With coverage report +make coverage-report # Open HTML coverage in browser +``` + +### Test Structure + +- **test_lpm.py** - 20 unit tests for LPM algorithm +- **test_concurrency.py** - 9 thread safety tests +- **test_service.py** - Integration tests + +### Coverage Requirements + +- **Minimum:** 35% +- **Target:** 50%+ +- **Check:** `make test-cov` shows coverage + +--- + +## ๐Ÿ”ง Code Quality + +### Linting + +```bash +make lint # Check with ruff +make format # Auto-format code +make type-check # Type checking with mypy +``` + +### Code Style + +- **Style Guide:** PEP 8 (via ruff) +- **Type Hints:** Required for all functions +- **Docstrings:** Google-style format + +**Example:** +```python +def lookup(ip: str) -> tuple[str, str, int]: + """ + Perform longest prefix match lookup. + + Args: + ip: IPv4 or IPv6 address string + + Returns: + Tuple of (prefix, next_hop, metric) + + Raises: + ValueError: If IP format is invalid + """ + # implementation... +``` + +--- + +## ๐Ÿ“‹ Pull Request Process + +1. **Fork** the repository +2. **Create** a feature branch: `git checkout -b feature/name` +3. **Make changes** following the guidelines +4. **Test** thoroughly: `make test-cov` +5. **Lint & format**: `make lint && make format` +6. **Type check**: `make type-check` +7. **Commit** with conventional commits +8. **Push** to your fork +9. **Create PR** with clear description + +### PR Description Template + +```markdown +## Description +Brief description of changes + +## Related Issues +Closes #123 + +## Changes +- Change 1 +- Change 2 + +## Testing +- [ ] Tests pass (29/29) +- [ ] Coverage maintained (โ‰ฅ35%) +- [ ] Linter passes +- [ ] Type checking passes + +## Types of Changes +- [ ] Bug fix +- [ ] New feature +- [ ] Breaking change +- [ ] Documentation +``` + +--- + +## ๐Ÿ› Reporting Issues + +### Bug Report + +```markdown +## Description +Clear description of the bug + +## Steps to Reproduce +1. Step 1 +2. Step 2 + +## Expected Behavior +What should happen + +## Actual Behavior +What actually happened + +## Environment +- Python version +- OS (Linux/Mac/Windows) +- Docker/K8s version (if applicable) + +## Logs/Output +Include relevant error messages +``` + +### Feature Request + +```markdown +## Description +Explain the feature and why it's needed + +## Use Case +When would this feature be useful? + +## Proposed Solution +How should it work? + +## Alternatives +Are there other approaches? +``` + +--- + +## ๐Ÿ“š Development Guidelines + +### Adding a New Feature + +1. **Create tests first** (TDD approach) +2. **Implement the feature** +3. **Ensure all tests pass** +4. **Update documentation** +5. **Add type hints** +6. **Write docstrings** + +### Modifying Existing Code + +1. **Run existing tests** - Ensure they pass +2. **Make changes** carefully +3. **Add/update tests** if behavior changed +4. **Check coverage** - Don't decrease it +5. **Update docs** if public API changed + +### Performance Considerations + +- Radix tree lookup is O(k) - don't add linear operations +- LRU cache is critical for performance - don't remove caching +- Thread safety must be maintained - use locks appropriately +- Memory usage is important - keep data structures efficient + +--- + +## ๐Ÿ” Code Review + +### What We Look For + +- โœ… **Correctness** - Does it work as intended? +- โœ… **Efficiency** - Are there performance concerns? +- โœ… **Readability** - Is the code clear and maintainable? +- โœ… **Testing** - Are there adequate tests? +- โœ… **Documentation** - Is it well documented? +- โœ… **Style** - Does it follow our conventions? + +### Review Process + +1. **Automated checks** - Linting, testing, coverage +2. **Code review** - Manual review by maintainers +3. **Feedback loop** - Address comments/suggestions +4. **Approval** - At least one approval required +5. **Merge** - Merge to main branch + +--- + +## ๐Ÿš€ Releasing + +### Semantic Versioning + +This project follows [Semantic Versioning](https://semver.org/): + +- **MAJOR** - Breaking API changes (v1.0.0 โ†’ v2.0.0) +- **MINOR** - New features (backward compatible) (v1.0.0 โ†’ v1.1.0) +- **PATCH** - Bug fixes (backward compatible) (v1.0.0 โ†’ v1.0.1) + +### Creating a Release + +1. **Update version** in relevant files +2. **Update CHANGELOG** with release notes +3. **Create git tag**: `git tag -a v1.0.0 -m "Release v1.0.0"` +4. **Push tag**: `git push origin v1.0.0` +5. **GitHub Actions** automatically creates release with: + - Python wheel and source distribution + - Docker images on ghcr.io + - Release notes on GitHub + +--- + +## ๐Ÿ“š Resources + +- **API Docs:** http://localhost:5000/docs (Swagger) +- **Algorithm Details:** See [README.md](README.md#-algorithm-details) +- **CI/CD Setup:** [.github/CICD_SETUP.md](.github/CICD_SETUP.md) +- **Project Structure:** See [README.md](README.md#-project-structure) + +--- + +## โ“ Questions? + +- **GitHub Issues** - Ask a question with `[question]` tag +- **Discussions** - Open GitHub Discussion for ideas +- **Email** - Contact maintainer for private matters + +--- + +## ๐Ÿ“„ Code of Conduct + +Be respectful, inclusive, and professional. We welcome contributors of all backgrounds and experience levels. + +--- + +**Thank you for contributing! ๐Ÿ™** diff --git a/README.md b/README.md index 3f89be5..6874fac 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,8 @@ make clean # Clean artifacts ## ๐Ÿ“ฆ Deployment +### Deployment Options + Multiple deployment options available: | Method | Use Case | Complexity | Setup Time | High Availability | @@ -104,21 +106,75 @@ Multiple deployment options available: | **Podman Systemd** | Single server prod | Medium | 10 min | No | | **Kubernetes** | Clustered prod | High | 30+ min | Yes | -**Quick Deploy:** +### Local Development (Podman Compose) ```bash -# Local development (recommended) make compose-up +``` + +**Details:** See [docker-compose.yml](docker-compose.yml) -# Production (single server with systemd) +### Production Single Server (Podman Systemd) + +```bash cp podman-systemd/*.service ~/.config/systemd/user/ systemctl --user enable --now routing-table-api.service +``` + +**Details:** See [podman-systemd/](podman-systemd/) + +### Production Cluster (Kubernetes) -# Kubernetes (production cluster) +```bash kubectl apply -f kubernetes-test.yaml ``` -**Deployment guides:** [docker-compose.yml](docker-compose.yml) | [podman-systemd/](podman-systemd/) | [kubernetes-test.yaml](kubernetes-test.yaml) +**What's included:** +- **ConfigMap** - Routes data configuration +- **Deployment** - 1 replica with health checks + - Liveness probe: 30s interval, 30s timeout + - Readiness probe: 10s interval, 20s initial delay + - Resource limits: 512Mi-2Gi memory, 500m-2000m CPU +- **Service** - ClusterIP exposure on port 5000 +- **Job** - Integration tests with wait-for-API init container +- **Ingress** (commented) - Optional external access with nginx + +**Configuration:** +- Mounts routes.txt as read-only volume +- Environment variables: `PYTHONUNBUFFERED=1`, `PROC_NUM=4` +- Image: `routing-table-api:latest` (local) +- Test image: `routing-table-api:test` + +**Prerequisites:** +- Kubernetes cluster (1.20+) +- Local images built: `podman build -t routing-table-api:latest .` +- Routes file at: `/path/to/routing-table-api/routes.txt` + +**Deployment:** + +```bash +# Update the hostPath in kubernetes-test.yaml to your routes.txt location +# Then apply: +kubectl apply -f kubernetes-test.yaml + +# Check status +kubectl get pods -l app=routing-table-api +kubectl logs -l app=routing-table-api -f + +# Run tests +kubectl get jobs +kubectl logs -l app=routing-table-api-tests -f + +# Port forward for local testing +kubectl port-forward svc/routing-table-api 5000:5000 +``` + +**Troubleshooting:** +- Pod stuck pending: Check resource availability (`kubectl describe node`) +- Tests failing: Verify routes.txt path and API readiness +- Connection refused: Ensure Service and Deployment are running + +**Details:** See [kubernetes-test.yaml](kubernetes-test.yaml) --- @@ -285,46 +341,7 @@ curl http://localhost:5000/metrics | grep cache ## ๐Ÿค Contributing -Contributions are welcome! Please follow these guidelines: - -### Quick Start - -```bash -# Fork and clone -git clone https://github.com/yourusername/routing-table-api.git -cd routing-table-api -make install - -# Create feature branch -git checkout -b feature/amazing-feature - -# Make changes and test -make test-cov # Must maintain โ‰ฅ35% coverage -make lint # Must pass -make type-check # Must pass - -# Commit using conventional commits -git commit -m "feat: add amazing feature" -``` - -### Requirements - -- โœ… All tests pass (29/29) -- โœ… Coverage maintained (โ‰ฅ35%) -- โœ… Linter passes (`make lint`) -- โœ… Type hints for new code -- โœ… Google-style docstrings -- โœ… Follow PEP 8 (enforced by ruff) - -### Commit Convention - -- `feat:` New feature -- `fix:` Bug fix -- `docs:` Documentation only -- `refactor:` Code refactoring -- `test:` Adding tests -- `chore:` Build/tooling -- `perf:` Performance improvement +Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines, requirements, and the commit convention. --- @@ -447,7 +464,6 @@ For production deployments requiring: **Contact:** Open GitHub issue with `[commercial]` tag - --- ## ๐Ÿ“„ License @@ -463,6 +479,7 @@ This project is open source and free to use. Sponsorship is optional and does no - **API Documentation:** http://localhost:5000/docs (Swagger) - **Alternative Docs:** http://localhost:5000/redoc - **CI/CD Setup:** [.github/CICD_SETUP.md](.github/CICD_SETUP.md) +- **Contributing:** [CONTRIBUTING.md](CONTRIBUTING.md) - **Issues & Bugs:** [GitHub Issues](https://github.com/weekmo/routing-table-api/issues) --- From 244c777fd8bf79c3180a07336a502582bad7b7c5 Mon Sep 17 00:00:00 2001 From: Mohammed Hassan Date: Mon, 12 Jan 2026 23:38:43 +0100 Subject: [PATCH 3/3] update readme, kubernates files --- CHANGELOG.md | 44 +++ CONTRIBUTING.md | 306 ++++++++++++++++++ README.md | 100 +----- ...rnetes-test.yaml => kubernetes-deploy.yaml | 69 ++-- kubernetes.yaml | 184 +++++++++++ 5 files changed, 579 insertions(+), 124 deletions(-) create mode 100644 CHANGELOG.md rename kubernetes-test.yaml => kubernetes-deploy.yaml (70%) create mode 100644 kubernetes.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..72f2184 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,44 @@ +# CHANGELOG + +All notable changes to this project are documented in this file. + +## [v0.2.0] - January 2026 + +### Added +- Radix tree implementation with O(k) lookup complexity +- LRU caching for sub-5ฮผs cached lookups +- Thread-safe concurrent operations +- Prometheus metrics integration +- Full IPv4 and IPv6 support +- CI/CD pipeline with automated testing and container publishing + +### Packaging +- Python wheel and source distribution are produced by CI +- Docker images are published to GitHub Container Registry: `ghcr.io/weekmo/routing-table-api` + +--- + +## Release Process + +Releases are created via GitHub Actions and include: + +1. Tag pushed (`v*.*.*`) triggers release workflow +2. Tests run across supported Python versions +3. Packages built (wheel and sdist) +4. Docker images pushed to `ghcr.io/weekmo/routing-table-api` +5. GitHub Release created with artifacts and notes + +To create a release locally: + +```bash +git tag -a v1.0.0 -m "Release v1.0.0" +git push origin v1.0.0 +``` + +For container deployments, CI publishes images to GitHub Container Registry. Use pinned tags in production (for example `ghcr.io/weekmo/routing-table-api:v1.0.0`) instead of `:latest` and set `imagePullPolicy` accordingly. + +--- + +## Versioning + +This project follows [Semantic Versioning](https://semver.org/). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 53c9fee..25da1ed 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -304,3 +304,309 @@ Be respectful, inclusive, and professional. We welcome contributors of all backg --- **Thank you for contributing! ๐Ÿ™** +# Contributing to Routing Table API + +Contributions are welcome! Please follow these guidelines to ensure high quality code and smooth collaboration. + +--- + +## ๐Ÿš€ Quick Start + +```bash +# Fork and clone +git clone https://github.com/yourusername/routing-table-api.git +cd routing-table-api +make install + +# Create feature branch +git checkout -b feature/amazing-feature + +# Make changes and test +make test-cov # Must maintain โ‰ฅ35% coverage +make lint # Must pass +make type-check # Must pass + +# Commit using conventional commits +git commit -m "feat: add amazing feature" +``` + +--- + +## โœ… Requirements + +All contributions must meet these criteria: + +- โœ… **All tests pass** (29/29) +- โœ… **Coverage maintained** (โ‰ฅ35%) +- โœ… **Linter passes** (`make lint`) +- โœ… **Type hints** for new code +- โœ… **Google-style docstrings** +- โœ… **PEP 8 compliance** (enforced by ruff) + +--- + +## ๐Ÿ“ Commit Convention + +Use [Conventional Commits](https://www.conventionalcommits.org/) format: + +| Type | Use Case | +|------|----------| +| `feat:` | New feature | +| `fix:` | Bug fix | +| `docs:` | Documentation only | +| `refactor:` | Code refactoring | +| `test:` | Adding/updating tests | +| `chore:` | Build/tooling changes | +| `perf:` | Performance improvement | + +**Examples:** +```bash +git commit -m "feat: add IPv6 support to radix tree" +git commit -m "fix: handle edge case in LPM lookup" +git commit -m "docs: add Kubernetes deployment guide" +git commit -m "test: add concurrency tests for thread safety" +``` + +--- + +## ๐Ÿงช Testing + +### Run All Tests + +```bash +make test # Run all 29 tests +make test-cov # With coverage report +make coverage-report # Open HTML coverage in browser +``` + +### Test Structure + +- **test_lpm.py** - 20 unit tests for LPM algorithm +- **test_concurrency.py** - 9 thread safety tests +- **test_service.py** - Integration tests + +### Coverage Requirements + +- **Minimum:** 35% +- **Target:** 50%+ +- **Check:** `make test-cov` shows coverage + +--- + +## ๐Ÿ”ง Code Quality + +### Linting + +```bash +make lint # Check with ruff +make format # Auto-format code +make type-check # Type checking with mypy +``` + +### Code Style + +- **Style Guide:** PEP 8 (via ruff) +- **Type Hints:** Required for all functions +- **Docstrings:** Google-style format + +**Example:** +```python +def lookup(ip: str) -> tuple[str, str, int]: + """ + Perform longest prefix match lookup. + + Args: + ip: IPv4 or IPv6 address string + + Returns: + Tuple of (prefix, next_hop, metric) + + Raises: + ValueError: If IP format is invalid + """ + # implementation... +``` + +--- + +## ๐Ÿ“‹ Pull Request Process + +1. **Fork** the repository +2. **Create** a feature branch: `git checkout -b feature/name` +3. **Make changes** following the guidelines +4. **Test** thoroughly: `make test-cov` +5. **Lint & format**: `make lint && make format` +6. **Type check**: `make type-check` +7. **Commit** with conventional commits +8. **Push** to your fork +9. **Create PR** with clear description + +### PR Description Template + +```markdown +## Description +Brief description of changes + +## Related Issues +Closes #123 + +## Changes +- Change 1 +- Change 2 + +## Testing +- [ ] Tests pass (29/29) +- [ ] Coverage maintained (โ‰ฅ35%) +- [ ] Linter passes +- [ ] Type checking passes + +## Types of Changes +- [ ] Bug fix +- [ ] New feature +- [ ] Breaking change +- [ ] Documentation +``` + +--- + +## ๐Ÿ› Reporting Issues + +### Bug Report + +```markdown +## Description +Clear description of the bug + +## Steps to Reproduce +1. Step 1 +2. Step 2 + +## Expected Behavior +What should happen + +## Actual Behavior +What actually happened + +## Environment +- Python version +- OS (Linux/Mac/Windows) +- Docker/K8s version (if applicable) + +## Logs/Output +Include relevant error messages +``` + +### Feature Request + +```markdown +## Description +Explain the feature and why it's needed + +## Use Case +When would this feature be useful? + +## Proposed Solution +How should it work? + +## Alternatives +Are there other approaches? +``` + +--- + +## ๐Ÿ“š Development Guidelines + +### Adding a New Feature + +1. **Create tests first** (TDD approach) +2. **Implement the feature** +3. **Ensure all tests pass** +4. **Update documentation** +5. **Add type hints** +6. **Write docstrings** + +### Modifying Existing Code + +1. **Run existing tests** - Ensure they pass +2. **Make changes** carefully +3. **Add/update tests** if behavior changed +4. **Check coverage** - Don't decrease it +5. **Update docs** if public API changed + +### Performance Considerations + +- Radix tree lookup is O(k) - don't add linear operations +- LRU cache is critical for performance - don't remove caching +- Thread safety must be maintained - use locks appropriately +- Memory usage is important - keep data structures efficient + +--- + +## ๐Ÿ” Code Review + +### What We Look For + +- โœ… **Correctness** - Does it work as intended? +- โœ… **Efficiency** - Are there performance concerns? +- โœ… **Readability** - Is the code clear and maintainable? +- โœ… **Testing** - Are there adequate tests? +- โœ… **Documentation** - Is it well documented? +- โœ… **Style** - Does it follow our conventions? + +### Review Process + +1. **Automated checks** - Linting, testing, coverage +2. **Code review** - Manual review by maintainers +3. **Feedback loop** - Address comments/suggestions +4. **Approval** - At least one approval required +5. **Merge** - Merge to main branch + +--- + +## ๐Ÿš€ Releasing + +### Semantic Versioning + +This project follows [Semantic Versioning](https://semver.org/): + +- **MAJOR** - Breaking API changes (v1.0.0 โ†’ v2.0.0) +- **MINOR** - New features (backward compatible) (v1.0.0 โ†’ v1.1.0) +- **PATCH** - Bug fixes (backward compatible) (v1.0.0 โ†’ v1.0.1) + +### Creating a Release + +1. **Update version** in relevant files +2. **Update CHANGELOG** with release notes +3. **Create git tag**: `git tag -a v1.0.0 -m "Release v1.0.0"` +4. **Push tag**: `git push origin v1.0.0` +5. **GitHub Actions** automatically creates release with: + - Python wheel and source distribution + - Docker images on ghcr.io + - Release notes on GitHub + +--- + +## ๐Ÿ“š Resources + +- **API Docs:** http://localhost:5000/docs (Swagger) +- **Algorithm Details:** See [README.md](README.md#-algorithm-details) +- **CI/CD Setup:** [.github/CICD_SETUP.md](.github/CICD_SETUP.md) +- **Project Structure:** See [README.md](README.md#-project-structure) + +--- + +## โ“ Questions? + +- **GitHub Issues** - Ask a question with `[question]` tag +- **Discussions** - Open GitHub Discussion for ideas +- **Email** - Contact maintainer for private matters + +--- + +## ๐Ÿ“„ Code of Conduct + +Be respectful, inclusive, and professional. We welcome contributors of all backgrounds and experience levels. + +--- + +**Thank you for contributing! ๐Ÿ™** diff --git a/README.md b/README.md index 6874fac..760cb01 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,9 @@ systemctl --user enable --now routing-table-api.service ### Production Cluster (Kubernetes) ```bash -kubectl apply -f kubernetes-test.yaml +# For testing manifests use `kubernetes-test.yaml`. +# For CI-built, production-ready images use `kubernetes-deploy.yaml` (pinned tags): +kubectl apply -f kubernetes-deploy.yaml ``` **What's included:** @@ -153,9 +155,10 @@ kubectl apply -f kubernetes-test.yaml **Deployment:** ```bash -# Update the hostPath in kubernetes-test.yaml to your routes.txt location -# Then apply: -kubectl apply -f kubernetes-test.yaml +# Update the hostPath in `kubernetes-deploy.yaml` to your `routes.txt` location or +# provision a PersistentVolume and PersistentVolumeClaim (`routes-pvc`). +# Then apply (use pinned IMAGE_TAG for production): +kubectl apply -f kubernetes-deploy.yaml # Check status kubectl get pods -l app=routing-table-api @@ -345,94 +348,11 @@ Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines --- -## ๐Ÿ“ฆ Releases +## ๐Ÿ“ฆ Releases & Changelog -### Latest Release: v0.2.0 - -**Release Date:** January 2026 - -**What's New:** -- โœจ Radix tree implementation with O(k) lookup complexity -- โšก LRU caching for sub-5ฮผs cached lookups -- ๐Ÿ”’ Thread-safe concurrent operations -- ๐Ÿ“Š Prometheus metrics integration -- ๐ŸŒ Full IPv4 and IPv6 support -- ๐Ÿงช Comprehensive test suite (29 tests, 39% coverage) -- ๐Ÿค– CI/CD pipeline with automated testing and security scans -- ๐Ÿ“ฆ Automated package distribution and container registry publishing - -### Download & Install - -**Container Images (Recommended):** -```bash -# Pull from GitHub Container Registry -podman pull ghcr.io/weekmo/routing-table-api:latest -podman pull ghcr.io/weekmo/routing-table-api:v0.2.0 - -# Run container -podman run -d -p 5000:5000 -v ./routes.txt:/app/routes.txt ghcr.io/weekmo/routing-table-api:latest -``` - -**Python Package (GitHub Releases):** -```bash -# Download from releases page -wget https://github.com/weekmo/routing-table-api/releases/download/v0.2.0/routing_table_api-0.2.0-py3-none-any.whl -pip install routing_table_api-0.2.0-py3-none-any.whl - -# Or install from source -pip install git+https://github.com/weekmo/routing-table-api.git@v0.2.0 -``` - -**Source Code:** -```bash -# Clone specific release -git clone --branch v0.2.0 https://github.com/weekmo/routing-table-api.git -cd routing-table-api -make install -``` - -### Release Assets - -Each release includes: -- ๐Ÿ“ฆ **Python wheel** (`.whl`) - Universal Python 3 package -- ๐Ÿ“„ **Source distribution** (`.tar.gz`) - Complete source code -- ๐Ÿณ **Docker images** - Multi-arch containers on ghcr.io -- ๐Ÿ“ **Release notes** - What's new and breaking changes - -### Automated Release Process - -Releases are automatically created via GitHub Actions: - -1. **Tag pushed** (`v*.*.*`) triggers the release workflow -2. **Tests run** - Ensures all 29 tests pass -3. **Packages built** - Creates wheel and source distribution -4. **GitHub Release created** - With release notes and artifacts -5. **Docker images pushed** - To GitHub Container Registry (ghcr.io) -6. **Tags applied** - Both version tag and `latest` - -**To create a release:** Push a semantic version tag: -```bash -git tag -a v0.3.0 -m "Release v0.3.0" -git push origin v0.3.0 -``` - -### Release Notes - -**All Releases:** [GitHub Releases](https://github.com/weekmo/routing-table-api/releases) - -**Container Registry:** [GitHub Packages](https://github.com/weekmo/routing-table-api/pkgs/container/routing-table-api) - -### Versioning - -This project follows [Semantic Versioning](https://semver.org/): - -- **MAJOR** version: Breaking API changes -- **MINOR** version: New features (backward compatible) -- **PATCH** version: Bug fixes (backward compatible) - -**Current:** `0.2.0` (Beta - API may change) -**Stable:** `1.0.0` (Coming Q2 2026) +Release notes, assets, and version history are maintained in `CHANGELOG.md` and GitHub Releases. CI/CD publishes packages and container images to GitHub Container Registry (`ghcr.io/weekmo/routing-table-api`). +For production deployments use pinned image tags (for example `ghcr.io/weekmo/routing-table-api:v1.0.0`) and set `imagePullPolicy` accordingly in `kubernetes.yaml`. --- ## ๐Ÿ’– Sponsor diff --git a/kubernetes-test.yaml b/kubernetes-deploy.yaml similarity index 70% rename from kubernetes-test.yaml rename to kubernetes-deploy.yaml index 62c80fc..c869fb8 100644 --- a/kubernetes-test.yaml +++ b/kubernetes-deploy.yaml @@ -1,15 +1,19 @@ --- -# ConfigMap for routes data +# Example Kubernetes deployment using CI/CD-built images from GHCR +# Replace `{{IMAGE_TAG}}` with the pinned tag produced by CI (for example v0.2.0) + apiVersion: v1 kind: ConfigMap metadata: name: routing-table-data namespace: default data: - # In production, use a PersistentVolume or external storage - # For testing, routes.txt would be mounted from host or built into image + # Do not store very large files here. Use a PVC or external storage for routes.txt. + example: | + # small example dataset + 192.0.2.0/24,198.51.100.1,10 + --- -# Deployment for the Routing Table API apiVersion: apps/v1 kind: Deployment metadata: @@ -18,7 +22,12 @@ metadata: labels: app: routing-table-api spec: - replicas: 1 + replicas: 2 + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% selector: matchLabels: app: routing-table-api @@ -29,8 +38,8 @@ spec: spec: containers: - name: api - image: routing-table-api:latest - imagePullPolicy: Never # Use local image for testing + image: ghcr.io/weekmo/routing-table-api:{{IMAGE_TAG}} + imagePullPolicy: IfNotPresent ports: - containerPort: 5000 name: http @@ -74,11 +83,10 @@ spec: value: "5000" volumes: - name: routes-data - hostPath: - path: /path/to/routing-table-api/routes.txt # Update to actual path - type: File + persistentVolumeClaim: + claimName: routes-pvc + --- -# Service to expose the API apiVersion: v1 kind: Service metadata: @@ -95,8 +103,8 @@ spec: name: http selector: app: routing-table-api + --- -# Job for running tests apiVersion: batch/v1 kind: Job metadata: @@ -114,8 +122,8 @@ spec: restartPolicy: Never containers: - name: tests - image: routing-table-api:test - imagePullPolicy: Never # Use local image for testing + image: ghcr.io/weekmo/routing-table-api:test + imagePullPolicy: IfNotPresent env: - name: API_URL value: "http://routing-table-api:5000" @@ -138,24 +146,17 @@ spec: sleep 5 done echo "API is ready!" + --- -# Optional: Ingress for external access (requires Ingress controller) -# apiVersion: networking.k8s.io/v1 -# kind: Ingress -# metadata: -# name: routing-table-api -# namespace: default -# annotations: -# nginx.ingress.kubernetes.io/rewrite-target: / -# spec: -# rules: -# - host: routing-api.local -# http: -# paths: -# - path: / -# pathType: Prefix -# backend: -# service: -# name: routing-table-api -# port: -# number: 5000 +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: routes-pvc + namespace: default +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + storageClassName: standard diff --git a/kubernetes.yaml b/kubernetes.yaml new file mode 100644 index 0000000..fc3b50d --- /dev/null +++ b/kubernetes.yaml @@ -0,0 +1,184 @@ +--- +# ConfigMap for routes data +apiVersion: v1 +kind: ConfigMap +metadata: + name: routing-table-data + namespace: default +data: + # In production, use a PersistentVolume or external storage + # For testing, routes.txt would be mounted from host or built into image +--- +# Deployment for the Routing Table API +apiVersion: apps/v1 +--- +# NOTE: This manifest is an example. CI/CD publishes images to +# `ghcr.io/weekmo/routing-table-api`. For production, pin image tags +--- +# NOTE: This manifest is an example. CI/CD publishes images to +# `ghcr.io/weekmo/routing-table-api`. For production, pin image tags +# (for example `ghcr.io/weekmo/routing-table-api:v1.0.0`) and set +# `imagePullPolicy` accordingly. + +# ConfigMap for routes data (not used for large datasets) +apiVersion: v1 +kind: ConfigMap +metadata: + name: routing-table-data + namespace: default +data: + # Do not store very large files here. Use a PVC or external storage for routes.txt. + example: | + # small example dataset + 192.0.2.0/24,198.51.100.1,10 + +--- +# Deployment for the Routing Table API +apiVersion: apps/v1 +kind: Deployment +metadata: + name: routing-table-api + namespace: default + labels: + app: routing-table-api +spec: + replicas: 2 + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% + selector: + matchLabels: + app: routing-table-api + template: + metadata: + labels: + app: routing-table-api + spec: + containers: + - name: api + image: ghcr.io/weekmo/routing-table-api:{{IMAGE_TAG}} # Replace with pinned tag (e.g. v0.2.0) + imagePullPolicy: IfNotPresent # Use `Always` for `:latest` + ports: + - containerPort: 5000 + name: http + protocol: TCP + volumeMounts: + - name: routes-data + mountPath: /app/routes.txt + subPath: routes.txt + readOnly: true + livenessProbe: + httpGet: + path: /health + port: 5000 + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /health + port: 5000 + initialDelaySeconds: 20 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + resources: + requests: + memory: "512Mi" + cpu: "500m" + limits: + memory: "2Gi" + cpu: "2000m" + env: + - name: PYTHONUNBUFFERED + value: "1" + - name: PROC_NUM + value: "4" + - name: HOST + value: "0.0.0.0" + - name: PORT + value: "5000" + volumes: + - name: routes-data + persistentVolumeClaim: + claimName: routes-pvc # Create a PVC named `routes-pvc` or update to your claim + +--- +# Service to expose the API +apiVersion: v1 +kind: Service +metadata: + name: routing-table-api + namespace: default + labels: + app: routing-table-api +spec: + type: ClusterIP + ports: + - port: 5000 + targetPort: 5000 + protocol: TCP + name: http + selector: + app: routing-table-api + +--- +# Job for running tests +apiVersion: batch/v1 +kind: Job +metadata: + name: routing-table-api-tests + namespace: default + labels: + app: routing-table-api-tests +spec: + backoffLimit: 2 + template: + metadata: + labels: + app: routing-table-api-tests + spec: + restartPolicy: Never + containers: + - name: tests + image: ghcr.io/weekmo/routing-table-api:test + imagePullPolicy: IfNotPresent + env: + - name: API_URL + value: "http://routing-table-api:5000" + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + initContainers: + - name: wait-for-api + image: busybox:1.36 + command: + - sh + - -c + - | + until wget -q --spider http://routing-table-api:5000/health; do + echo "Waiting for API to be ready..." + sleep 5 + done + echo "API is ready!" + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: routes-pvc + namespace: default +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + storageClassName: standard