Skip to content

Commit 36e9847

Browse files
authored
Merge pull request #287 from finos/chore/provider-onboarding-prep
chore(providers): onboarding prep — unblock Azure/GCP/OCI merges
2 parents 542a7de + 42688a0 commit 36e9847

87 files changed

Lines changed: 3433 additions & 386 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci-tests.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,26 @@ jobs:
319319
ls -la .coverage* 2>/dev/null || echo "no .coverage data files found"
320320
321321
- name: Combine coverage and enforce combined threshold
322+
id: coverage-gate
322323
run: make ci-tests-coverage-check
323324

325+
# Single deterministic Codecov upload: the combined report from ALL legs is
326+
# sent once here, instead of each leg uploading separately. Codecov then
327+
# shows one stable number (no mid-run partial-merge flapping) and this needs
328+
# no hardcoded leg count — it auto-scales to any number of provider legs.
329+
# if: always() so the report uploads even when the threshold gate fails
330+
# above (the job still fails on the gate; Codecov still gets the data).
331+
- name: Upload combined coverage to Codecov
332+
if: always()
333+
continue-on-error: true
334+
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v6
335+
with:
336+
token: ${{ secrets.CODECOV_TOKEN }}
337+
files: coverage-combined.xml
338+
name: combined
339+
# Overwrite any prior partial report for this commit with the full merge.
340+
override_commit: ${{ github.event.pull_request.head.sha || github.sha }}
341+
324342
# Single stable required status check for branch protection. Replaces listing
325343
# every individual job name (which drifts silently when jobs are renamed).
326344
# Register "CI Passed" as the sole required check in branch protection.

.github/workflows/reusable-test.yml

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -185,15 +185,12 @@ jobs:
185185
coverage-*.xml
186186
.coverage*
187187
188-
- name: Upload coverage to Codecov
189-
if: inputs.upload-coverage && always()
190-
continue-on-error: true
191-
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v6
192-
with:
193-
token: ${{ secrets.CODECOV_TOKEN }}
194-
files: coverage-${{ inputs.test-type }}${{ inputs.provider != '' && format('-{0}', inputs.provider) || '' }}.xml
195-
flags: ${{ inputs.test-type }}${{ inputs.provider != '' && format('-{0}', inputs.provider) || '' }}
196-
name: ${{ inputs.test-type }}${{ inputs.provider != '' && format('-{0}', inputs.provider) || '' }}-tests
188+
# NOTE: per-leg coverage is NOT uploaded to Codecov here. The .coverage
189+
# data files are uploaded as artifacts (above) and the coverage-combine
190+
# fan-in job in ci-tests.yml merges them and uploads ONE combined report
191+
# to Codecov. A single upload gives Codecov one deterministic number
192+
# (no mid-run partial-merge flapping) and auto-scales to any number of
193+
# provider legs without a hardcoded after_n_builds count.
197194

198195
- name: Upload test results to Codecov
199196
# Gated on upload-coverage: legs without pytest (e.g. ui-smoke) produce

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,36 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1010

1111
### BREAKING CHANGES
1212

13+
- **`Template.instance_type` renamed to `Template.machine_type`.**
14+
The `instance_type` field on the `Template` domain model has been renamed
15+
to `machine_type` to reflect provider-neutral terminology. The old name
16+
is accepted as a write-only alias (via `AliasChoices`) for backward
17+
compatibility with existing YAML / JSON configuration files, but it emits
18+
a `DeprecationWarning` when used in Python code. Update template
19+
definitions to use `machine_type` at your earliest convenience.
20+
21+
- **`Template.instance_profile` renamed to `Template.machine_role`.**
22+
The `instance_profile` field has been renamed to `machine_role` for the
23+
same provider-neutral reason. The old name is accepted as a write-only
24+
alias with a `DeprecationWarning`, just like `instance_type` above.
25+
Update template definitions to use `machine_role`.
26+
27+
- **`Template.provider_data` field removed.**
28+
The `provider_data` catch-all dict has been deleted. Provider-specific
29+
data is now carried on typed subclasses (`AWSTemplate`, `K8sTemplate`,
30+
etc.). Code that reads or writes `template.provider_data` must be
31+
migrated to the appropriate typed subclass field.
32+
33+
### Deprecated aliases
34+
35+
The following backward-compat aliases are available on the `Template` model
36+
and will be removed in the next major release:
37+
38+
| Old name | New name | Notes |
39+
|---|---|---|
40+
| `instance_type` | `machine_type` | Accepted by `model_validate` and `__init__`; emits `DeprecationWarning` in Python |
41+
| `instance_profile` | `machine_role` | Accepted by `model_validate` and `__init__`; emits `DeprecationWarning` in Python |
42+
1343
- **Daemon token file at `<work_dir>/server/orb-server.token`.**
1444
When `orb server start` is invoked (daemon or `--foreground` mode), a
1545
random bearer token is written to `<work_dir>/server/orb-server.token`

README.md

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,24 @@
1313
</p>
1414

1515
<p align="center">
16-
<a href="https://pypi.org/project/orb-py/"><img src="https://img.shields.io/pypi/v/orb-py" alt="PyPI Version"></a>
17-
<a href="https://pypi.org/project/orb-py/"><img src="https://img.shields.io/pypi/pyversions/orb-py" alt="Python Versions"></a>
1816
<a href="LICENSE"><img src="https://img.shields.io/github/license/finos/open-resource-broker" alt="License"></a>
17+
<a href="https://pypi.org/project/orb-py/"><img src="https://img.shields.io/pypi/v/orb-py" alt="PyPI Version"></a>
1918
<a href="https://github.com/finos/open-resource-broker/releases"><img src="https://img.shields.io/github/v/release/finos/open-resource-broker" alt="Latest Release"></a>
19+
<a href="https://pypi.org/project/orb-py/"><img src="https://img.shields.io/pypi/pyversions/orb-py" alt="Python Versions"></a>
2020
<a href="https://deepwiki.com/finos/open-resource-broker"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
21-
</p>
22-
23-
<p align="center">
21+
<br>
2422
<a href="https://github.com/finos/open-resource-broker/actions/workflows/ci-tests.yml"><img src="https://github.com/finos/open-resource-broker/actions/workflows/ci-tests.yml/badge.svg" alt="Unit Tests"></a>
2523
<a href="https://github.com/finos/open-resource-broker/actions/workflows/ci-quality.yml"><img src="https://github.com/finos/open-resource-broker/actions/workflows/ci-quality.yml/badge.svg" alt="Quality Checks"></a>
2624
<a href="https://github.com/finos/open-resource-broker/actions/workflows/security-code.yml"><img src="https://github.com/finos/open-resource-broker/actions/workflows/security-code.yml/badge.svg" alt="Security Scanning"></a>
27-
<a href="https://codecov.io/gh/finos/open-resource-broker"><img src="https://codecov.io/gh/finos/open-resource-broker/graph/badge.svg" alt="Coverage"></a>
2825
<a href="https://github.com/finos/open-resource-broker/actions/workflows/docs.yml"><img src="https://github.com/finos/open-resource-broker/actions/workflows/docs.yml/badge.svg" alt="Documentation"></a>
26+
<br>
27+
<a href="https://codecov.io/gh/finos/open-resource-broker"><img src="https://codecov.io/gh/finos/open-resource-broker/graph/badge.svg" alt="Coverage"></a>
28+
<a href="https://scorecard.dev/viewer/?uri=github.com/finos/open-resource-broker"><img src="https://api.securityscorecards.dev/projects/github.com/finos/open-resource-broker/badge" alt="OpenSSF Scorecard"></a>
2929
</p>
3030

3131
---
3232

33-
ORB is a unified API for orchestrating and provisioning compute capacity programmatically. Define what you need in a template, request it, track it, return it — through a CLI, REST API, Python SDK, or MCP server.
33+
Open Resource Broker (ORB) is a unified API for orchestrating and provisioning compute capacity programmatically. Define what you need in a template, request it, track it, return it — through a CLI, REST API, Python SDK, or MCP server.
3434

3535
Built for AWS today (EC2, Auto Scaling Groups, SpotFleet, EC2Fleet), with an extensible provider system for adding new cloud backends.
3636

@@ -192,6 +192,31 @@ See the [AWS Provider Guide](docs/root/user_guide/configuration.md) for required
192192

193193
</details>
194194

195+
<details>
196+
<summary>Kubernetes Provider Setup</summary>
197+
198+
ORB uses the standard Kubernetes client credential chain — any context that works with `kubectl` works with ORB. Running in-cluster, it auto-detects the mounted service account; running outside, it reads your `KUBECONFIG` and current context.
199+
200+
```bash
201+
# Verify your context is active
202+
kubectl config current-context
203+
```
204+
205+
**Supported credential methods:** `KUBECONFIG` contexts, the default `~/.kube/config`, and in-cluster service accounts (auto-detected). See [provider discovery](docs/root/providers/k8s/discovery.md) for the detection order.
206+
207+
### Supported resource types
208+
209+
| Type | Description |
210+
|---|---|
211+
| `Pod` | Single-pod provisioning |
212+
| `Deployment` | Replica-managed stateless workloads |
213+
| `StatefulSet` | Stable-identity stateful workloads |
214+
| `Job` | Run-to-completion batch workloads |
215+
216+
Install with `pip install "orb-py[k8s]"`. Minimum RBAC is in [`docs/root/providers/k8s/rbac.yaml`](docs/root/providers/k8s/rbac.yaml); see the [Kubernetes Provider Guide](docs/root/providers/k8s/index.md) for full setup.
217+
218+
</details>
219+
195220
## Interfaces
196221

197222
ORB provides four ways to interact with your infrastructure.
@@ -377,7 +402,8 @@ curl http://localhost:8000/health
377402

378403
</details>
379404

380-
## Symphony HostFactory on Kubernetes (legacy)
405+
<details>
406+
<summary>Symphony HostFactory on Kubernetes (legacy)</summary>
381407

382408
The `k8s-legacy` module is a Symphony HostFactory custom provider plugin for Kubernetes, predating the modern multi-cloud ORB architecture. It is now bundled with `orb-py` as an optional install extra rather than as a separate PyPI package.
383409

@@ -398,6 +424,8 @@ The plugin is in maintenance mode. A modern Kubernetes provider with native ORB
398424
- **Upgrading from `open-resource-broker`?** See the [migration guide](docs/root/operational/from-open-resource-broker.md).
399425
- **Deploying the Symphony HF plugin?** See the [k8s-legacy deployment guide](k8s-legacy/README.md).
400426

427+
</details>
428+
401429
## Project
402430

403431
Architecture, development, and documentation.

codecov.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# Coverage is uploaded to Codecov exactly ONCE, from the ci-tests
2+
# coverage-combine job, which merges every leg's .coverage data first. A single
3+
# upload gives Codecov one deterministic number (no mid-run partial-merge
4+
# flapping) and needs no hardcoded leg count — it auto-scales to any number of
5+
# provider legs.
16
coverage:
27
status:
38
project:
@@ -22,7 +27,14 @@ flags:
2227
- src/orb/
2328
carryforward: true
2429

30+
# Kept in sync with [tool.coverage.run] omit in pyproject.toml so Codecov's
31+
# view matches the combined report we upload.
2532
ignore:
2633
- "tests/"
2734
- "docs/"
2835
- "scripts/"
36+
- "src/orb/k8s_legacy/**" # own separate test suite; runs outside the main legs
37+
- "**/tests/**" # test files under src/ are not production source
38+
- "src/orb/__main__.py" # thin process entrypoints — not unit-tested
39+
- "src/orb/run.py"
40+
- "src/orb/interface/server_daemon.py"

config/default_config.json

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,6 @@
4141
"audit_logs": "audit_logs",
4242
"metrics_logs": "metrics_logs"
4343
},
44-
"fleet_types": {
45-
"instant": "instant",
46-
"request": "request",
47-
"maintain": "maintain"
48-
},
49-
"price_types": {
50-
"ondemand": "ondemand",
51-
"spot": "spot",
52-
"heterogeneous": "heterogeneous"
53-
},
5444
"statuses": {
5545
"request": {
5646
"pending": "pending",
@@ -80,22 +70,14 @@
8070
}
8171
},
8272
"patterns": {
83-
"ec2_instance": "^i-[a-f0-9]+$",
84-
"spot_fleet": "^sfr-[a-f0-9]+$",
85-
"ec2_fleet": "^fleet-[a-f0-9]+$",
86-
"asg": "^[a-zA-Z0-9_-]+$",
87-
"instance_type": "^[a-z][0-9][a-z]?\\.[a-z0-9]+$",
88-
"region": "^[a-z]{2}-[a-z]+-\\d$",
89-
"request_id": "^(req|ret)-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
73+
"request_id": "^(req|ret)-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
74+
"tag_key": "^[\\w\\s+=.@-]+$",
75+
"cidr_block": "^(\\d{1,3}\\.){3}\\d{1,3}/\\d{1,2}$"
9076
},
9177
"prefixes": {
9278
"default": "",
9379
"request": "req-",
9480
"return_prefix": "ret-",
95-
"launch_template": "",
96-
"instance": "",
97-
"fleet": "",
98-
"asg": "",
9981
"tag": ""
10082
}
10183
},
@@ -214,14 +196,15 @@
214196
},
215197
"server": {
216198
"enabled": false,
217-
"host": "0.0.0.0",
199+
"host": "127.0.0.1",
218200
"port": 8000,
219201
"workers": 1,
220202
"reload": false,
221203
"docs_enabled": true,
222204
"docs_url": "/docs",
223205
"redoc_url": "/redoc",
224206
"openapi_url": "/openapi.json",
207+
"trusted_hosts": ["localhost", "127.0.0.1", "::1", "testserver"],
225208
"auth": {
226209
"enabled": false,
227210
"strategy": "none",
@@ -234,7 +217,7 @@
234217
"cors": {
235218
"enabled": true,
236219
"origins": [
237-
"*"
220+
"http://localhost:8000"
238221
],
239222
"methods": [
240223
"GET",
@@ -270,12 +253,7 @@
270253
"default": "",
271254
"request": "req-",
272255
"return_prefix": "ret-",
273-
"launch_template": "",
274-
"instance": "",
275-
"fleet": "",
276-
"spot_fleet": "",
277-
"asg": "",
278256
"tag": ""
279257
}
280258
}
281-
}
259+
}

dev-tools/package/generate_pyproject.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -230,19 +230,20 @@ def update_pyproject_selective(pyproject_path: Path) -> None:
230230
continue
231231

232232
elif line.startswith("[tool.coverage.run]"):
233-
# Replace coverage source with package_root from .project.yml
234-
new_lines.extend(
235-
[
236-
"[tool.coverage.run]",
237-
f'source = ["{package_root}"]',
238-
"branch = true",
239-
"",
240-
]
241-
)
242-
# Skip existing coverage.run section
233+
# Re-template only source (from package_root) + branch; preserve every
234+
# other hand-authored key in this section (e.g. omit) instead of
235+
# dropping it — the generator manages source, not the whole policy.
243236
i += 1
237+
preserved = []
244238
while i < len(lines) and not lines[i].strip().startswith("["):
239+
existing = lines[i].strip()
240+
if not existing.startswith("source ") and not existing.startswith("branch "):
241+
preserved.append(lines[i])
245242
i += 1
243+
new_lines.append("[tool.coverage.run]")
244+
new_lines.append(f'source = ["{package_root}"]')
245+
new_lines.append("branch = true")
246+
new_lines.extend(preserved)
246247
continue
247248

248249
else:

docs/mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ nav:
136136
- Performance Optimization: developer_guide/performance-optimization.md
137137
- Development Setup: developer_guide/development.md
138138
- BaseSetting Providers: developer_guide/basesettings-providers.md
139+
- Deprecating Fields & APIs: developer_guide/deprecation.md
139140
- Changelog Management: developer_guide/changelog_management.md
140141
- Releases: developer_guide/releases.md
141142
- Testing: developer_guide/testing.md

docs/root/cli/template-commands.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ Templates can be defined in JSON or YAML format:
7575
"name": "My AWS Template",
7676
"provider_api": "aws",
7777
"image_id": "ami-12345678",
78-
"instance_type": "t3.medium",
78+
"machine_type": "t3.medium",
7979
"key_name": "my-keypair",
8080
"security_group_ids": ["sg-12345678"],
8181
"subnet_ids": ["subnet-12345678"],
@@ -94,7 +94,7 @@ template_id: my-aws-template
9494
name: My AWS Template
9595
provider_api: aws
9696
image_id: ami-12345678
97-
instance_type: t3.medium
97+
machine_type: t3.medium
9898
key_name: my-keypair
9999
security_group_ids:
100100
- sg-12345678
@@ -179,7 +179,7 @@ $ orb templates list
179179
"name": "Basic AWS Template",
180180
"provider_api": "aws",
181181
"image_id": "ami-12345678",
182-
"instance_type": "t3.medium"
182+
"machine_type": "t3.medium"
183183
}
184184
],
185185
"total_count": 1,
@@ -195,7 +195,7 @@ $ orb templates show aws-basic
195195
"name": "Basic AWS Template",
196196
"provider_api": "aws",
197197
"image_id": "ami-12345678",
198-
"instance_type": "t3.medium",
198+
"machine_type": "t3.medium",
199199
"key_name": "my-keypair",
200200
"security_group_ids": ["sg-12345678"],
201201
"subnet_ids": ["subnet-12345678"],
@@ -248,7 +248,7 @@ $ orb templates validate --file invalid-template.json
248248
"valid": false,
249249
"validation_errors": [
250250
"Missing required field: template_id",
251-
"Invalid instance_type: must be valid EC2 instance type"
251+
"Invalid machine_type: must be a valid instance type"
252252
],
253253
"validation_warnings": [
254254
"No tags specified: recommended for resource management"
@@ -295,7 +295,7 @@ $ orb templates refresh
295295
```bash
296296
$ orb templates list --format table
297297
+---------------+--------------------+------------+---------------+-------------+
298-
│ Template ID │ Name │ Provider API │ Image ID │ Instance Type │
298+
│ Template ID │ Name │ Provider API │ Image ID │ Machine Type
299299
+---------------+--------------------+------------+---------------+-------------+
300300
│ aws-basic │ Basic AWS Template │ aws │ ami-12345678 │ t3.medium │
301301
│ aws-spot │ Spot Instance Temp │ aws │ ami-87654321 │ t3.large │

0 commit comments

Comments
 (0)