Skip to content

feat: Add Azure and GCP support#240

Closed
IkeM-L wants to merge 2626 commits into
finos:mainfrom
hmxlabs:IML/azure-gcp-async
Closed

feat: Add Azure and GCP support#240
IkeM-L wants to merge 2626 commits into
finos:mainfrom
hmxlabs:IML/azure-gcp-async

Conversation

@IkeM-L

@IkeM-L IkeM-L commented Jun 9, 2026

Copy link
Copy Markdown

Description

Adds first-class Azure and GCP provider support and wires both providers into
the shared ORB provider registration, configuration, template, CLI, validation,
and lifecycle paths.

Changes in this branch:

  • Adds Azure provider implementation covering provider config, template
    extensions, CLI spec, registration, credential/session handling, Azure client
    integration, VMSS/SingleVM/CycleCloud handlers, ARM payload mapping, metadata
    handling, inventory/status, provisioning, termination, VMSS cleanup, health
    checks, and Azure-specific exceptions.
  • Adds GCP provider implementation covering provider config, template
    extensions, CLI spec, registration, Compute Engine client integration,
    SingleVM/MIG handlers, operation contexts, provisioning, mutation, inventory,
    health checks, dry-run support, image resolution, and typed GCP exception
    translation.
  • Registers AWS, Azure, and GCP provider types through the shared provider
    registry and loads provider defaults without relying on eager provider
    registry bootstrap.
  • Extends orb provider add/update and orb init CLI surfaces for Azure and
    GCP provider configuration.
  • Adds provider-neutral spot placement planning and execution services, plus AWS
    and Azure spot placement score adapters (GCP has no equivalent abstraction).
  • Updates request provisioning/status flows for async provider execution,
    timeout handling, partial fulfillment, planned spot shortfalls, provider
    metadata propagation, and provider follow-up cleanup states.
  • Preserves provider-specific template data through the template DTO boundary
    instead of flattening AWS-only fields into shared metadata.
  • Updates architecture/developer docs for the provider and dependency-injection
    patterns changed by this work.

Examples of bug fixes to pre-existing code landed in this branch:

  • Machine sync persisted the wrong resource_id on discovered machines: the
    prior code always took request.resource_ids[0] as the fallback rather than
    preferring the instance's own resource identity. Multi-resource Azure
    acquires surfaced the bug, but the fix improves correctness in general.
  • CLI stdout for structured output was routed through the rich console, which
    wrapped long strings and broke consumers parsing stdout as JSON; structured
    output now also honors --quiet.

Diff size: 243 files changed, 36,504 insertions, 481 deletions.

Type of Change

  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Code cleanup or refactor
  • Documentation update
  • Dependencies update
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Performance improvement
  • CI/CD or build process changes

Related Issues

N/A

How Has This Been Tested?

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed

Test coverage added/updated in this branch includes:

  • Azure provider tests for config, credentials, client behavior, deployment
    service, native spec service, network identity resolution, resource manager,
    strategy lifecycle/status, template validation, CycleCloud handling, VMSS
    cleanup, VMSS handler behavior, SingleVM handler behavior, SSH key resolution,
    error utilities, import guards, and async service behavior.
  • GCP provider tests for CLI spec, provider registration, config validation,
    Compute client behavior, disk types, runtime behavior, strategy CLI defaults,
    and service account scopes.
  • Application/service tests for provisioning orchestration, deprovisioning,
    machine sync, request status management, request query behavior, spot placement
    planning/execution, request creation/sync handlers, template command/query
    handlers, and acquire/return orchestration.
  • Registry/bootstrap/config/template tests for provider registration, provider
    defaults, init handling, field mapping, template DTO round-tripping, and
    provider import boundaries.

Checks run:

  • uv run pyright
    • Result: passed, 0 errors, 0 warnings, 0 informations.
  • uv run pytest --no-cov -q -ra
    • Result: 7,529 passed, 99 skipped, 0 failed, 64 warnings.
  • uv run pytest --no-cov -q -ra -o addopts='--tb=short --strict-markers' tests/onmoto/test_cli_onmoto.py
    • Result: passed, 20 passed, 1 warning.
  • In-house harness used to test Azure and GCP VM creation/polling/deletion with all new handlers.

Test Configuration

  • Python version: project targets Python 3.12 for pyright; package requires Python >=3.10
  • OS: Darwin 25.3.0 arm64
  • AWS region: N/A
  • Dependencies changed: yes. Adds httpx, Azure SDK packages, google-cloud-compute, and a dev pytest dependency entry; updates uv.lock.

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published
  • I have updated the CHANGELOG.md file
  • I have updated the version number (if applicable)

Additional Notes

  • Azure CycleCloud auth is modeled through credential paths rather than inline
    username/password or bearer-token fields.
  • GCP auth is modeled around Application Default Credentials.
  • Provider-specific template fields are retained as provider config at the DTO
    boundary so shared template DTOs do not need AWS/Azure/GCP-specific fields for
    every provider knob.
  • Changelog and version number not updated pending discussion about whether this justifies a new version

Screenshots (if appropriate)

N/A

Performance Impact

  • No significant performance impact
  • Performance improved
  • Performance degraded (explain why it's necessary)

Provider operations now use async orchestration paths where applicable. GCP's
synchronous SDK execution is isolated behind the async provider contract with
worker-thread execution.

Security Considerations

  • No security implications
  • Security improved
  • Potential security concerns (explain and justify)

Security-relevant changes:

New surface added for the new providers:

  • Azure CycleCloud inline username/password and bearer-token config is rejected;
    callers must use credential references.
  • Provider credential material is kept out of template metadata and provider
    context in the new Azure paths.
  • GCP uses ADC rather than adding a raw credential config surface.

Dependencies

New runtime dependencies:

  • httpx>=0.27.0
  • azure-core>=1.38.2
  • azure-identity>=1.25.2
  • azure-mgmt-authorization>=4.0.0
  • azure-mgmt-compute>=37.2.0
  • azure-mgmt-monitor>=7.0.0
  • azure-mgmt-msi>=7.1.0
  • azure-mgmt-network>=30.2.0
  • azure-mgmt-resource>=25.0.0
  • azure-mgmt-resource-subscriptions>=1.0.0b1
  • google-cloud-compute>=1.39.0

New dev dependency entry:

  • pytest>=9.0.2

Migration Guide

N/A

Deployment Notes

  • Azure provider instances require Azure subscription/resource-group
    configuration and Azure credentials resolvable by the runtime environment.
  • Azure CycleCloud integration requires a CycleCloud URL and credential path
    when using CycleCloud-backed templates.
  • GCP provider instances require project_id, region, and Application Default
    Credentials available in the runtime environment.

Reviewers

@finos/open-resource-broker-maintainers

fgogolli and others added 30 commits March 31, 2026 23:10
fix(config): config_dict path now applies full normalisation pipeline
…ithub-actions-20326145ca

ci(deps): bump the github-actions group with 5 updates
…intenance

chore: monthly dependency maintenance
Adds 33 new onmoto tests covering previously untested price type and
release scenarios for the three main AWS fleet handlers:

- test_ec2fleet_price_types.py (13 tests): spot + heterogeneous price
  types across instant/maintain/request fleet types; release capacity
  decrement; partial release; multi-AZ subnets; tag propagation
- test_asg_price_types.py (9 tests): spot + heterogeneous price types;
  release with capacity decrement; partial release; tag propagation;
  multi-instance types; min/max/desired capacity assertions
- test_spot_fleet_gaps.py (11 tests): on-demand + heterogeneous price
  types; maintain/request fleet cancellation on full release; partial
  release capacity decrement; allocation strategy; expiry time; tags

274 onmoto tests passing. Pre-existing failures in test_sdk_onmoto.py
are unrelated to these changes (confirmed on main).

Closes tasks: open-resource-broker-2147, open-resource-broker-2148, open-resource-broker-2149
…in CI

Fixes 440 pyright errors across 87 test files, reducing to 0. Changes:

- Phase 0: ruff auto-fix pass (already clean)
- Phase 1: fix reportMissingImports, reportUnhashable, None guards
- Phase 2: fix reportAbstractUsage, MagicMock(**dict) patterns
- Phase 3: fix reportArgumentType, reportAttributeAccessIssue across all
  remaining files — SimpleNamespace → typed domain objects, boto3 client
  mock assignments via cast(Any, ...), private attr access via public API

Also enforces pyright on tests/ in CI:
- pyproject.toml: expand include from src/orb + 2 test dirs to all tests/
  and remove 4 previously-excluded onmoto files (now clean)
- makefiles/ci.mk: change pyright target from src/ to src/ tests/

5986 tests passing, 4 pre-existing failures in test_sdk_onmoto.py
(confirmed on main before these changes)
…onaws fixes

Adds remaining tests for epic open-resource-broker-1670:

- test_run_instances_gaps.py (9 tests): tags, user_data, key_name, IAM
  instance profile propagation; stopped/terminated check_status; two-step
  partial release; idempotent release; invalid subnet error handling
- test_cross_cutting.py (6 tests): provider_data.resource_type for ASG
  and RunInstances; LT tag verification (orb:request-id, orb:managed-by)
  with real AWSLaunchTemplateManager; idempotency for RunInstances and
  EC2Fleet
- test_cleanup_e2e_onaws.py: add TestRunInstancesCleanupE2E class
- test_onaws.py: re-enable EC2Fleet+maintain+ondemand/spot in
  DEFAULT_ATTRIBUTE_COMBINATIONS; add partial return cases for
  EC2Fleet+request and SpotFleet+request

Closes tasks: open-resource-broker-2150, open-resource-broker-2151, open-resource-broker-2152
…rapping

Rich Console() with no arguments auto-detects terminal width and defaults
to 80 chars when stdout is not a TTY (subprocess context). This caused
JSON output to be line-wrapped at 80 chars, inserting literal newlines
into JSON string values (e.g. status messages), making the output
unparseable by hfmock._parse_json_response.

Changes:
- main.py: use plain print() instead of print_info() for JSON output —
  JSON must never go through Rich's line-wrapping console
- console.py: set Console width to max int when not a TTY, and disable
  colour — prevents line wrapping in subprocess/pipe contexts

This fixes test_partial_return_reduces_capacity failures where
_wait_for_return_completion received KeyError: 'requests' because the
status response JSON was corrupted by Rich line wrapping.
…rage

Closes all 8 gaps identified in the onmoto/onaws equivalence analysis:

- test_partial_return.py: TestPartialReturnCapacityReduction — ASG/EC2Fleet/
  SpotFleet capacity decrement assertions after partial return
- test_multi_resource_onmoto.py: multi-RunInstances and multi-ASG batch return
- test_sdk_onmoto.py: get_request_status(ret-xxx) response shape regression
  guard; extended provider API scenario breadth (EC2Fleet/ASG/SpotFleet)
- test_cleanup_e2e.py: TestCleanupViaOrchestrator — full return via CQRS
  buses deletes LT and terminates instance
- test_rest_api_onmoto.py: pagination (limit/offset) and concurrent requests
- test_mcp_onmoto.py: list_return_requests reaches complete status
- test_cli_onmoto.py: narrow terminal (COLUMNS=40/20) JSON output validity
  regression test for Rich line-wrapping fix
…eturn

Regression introduced in d7e804e which added fleet deletion for request
fleets after any instance termination — including partial returns. This
caused the fleet to enter deleted_running state with orphaned instances.

Fixes:
- Extract FleetReleaseDecision value object in fleet_release_policy.py
  encoding requires_capacity_reduction, has_fleet_record, is_full_return
- EC2Fleet request partial return: terminate instances only, no fleet
  deletion, no LT cleanup (fire-and-forget, AWS won't replace instances)
- EC2Fleet request full return: delete fleet + cleanup LT (unchanged)
- EC2Fleet maintain full return: use _delete_fleet() not delete_fleets()
  directly (preserves TerminateInstances=True semantics)
- SpotFleet request partial return: terminate instances only, no cancel
- SpotFleet maintain: fix enum vs string comparison bug (AWSFleetType
  enum was never matching the lowercase string from fleet config)
- Both release managers now use compute_fleet_release_decision() instead
  of inline fleet_type branching

Closes: open-resource-broker-2162, open-resource-broker-2163
…leanup_e2e.py

Addresses GitHub code quality bot comments on PR finos#190.
…rom main

- Auto-fix 17 ruff I001/F401 errors in test files introduced by pyright fixes
- Restore uv.lock from main to fix nltk 3.9.3→3.9.4 CVE regression
  caused by conflict resolution taking wrong side during rebase
- Rename _partial_return_cases() to _partial_return_capacity_cases()
  removing request fleet cases — request fleets are fire-and-forget and
  do not reduce capacity on partial return
- Add _partial_return_fire_and_forget_cases() covering EC2Fleet request,
  SpotFleet request, and RunInstances
- Add test_partial_return_terminates_instance_only asserting correct
  behaviour for fire-and-forget partial returns: instance terminated,
  remaining instances still running, capacity unchanged, fleet not deleted

This fixes 14 failing onaws test_partial_return_reduces_capacity cases
for EC2Fleet.Request and SpotFleet.Request scenarios.
…use config-driven paths

- Add 4 test files to pyright exclude list — they intentionally import
  non-existent modules inside pytest.raises(ImportError) to verify
  deleted code is gone; pyright cannot distinguish this intent
- Change ci-quality-pyright to run pyright without explicit paths so
  the exclude list in pyproject.toml is respected
…nces provisioned

Weighted multi-type EC2Fleet request scenarios can fulfil 4 units of
weighted capacity with a single high-weight instance (e.g. t2.medium
weight=4). This is correct AWS behaviour, not a flaky infra issue.
Replace assert with pytest.skip so the test is skipped gracefully
rather than failing when weighted capacity results in 1 physical instance.
@IkeM-L IkeM-L requested a review from a team as a code owner June 9, 2026 08:48
@fgogolli fgogolli changed the title Add Azure and GCP support feat: Add Azure and GCP support Jun 11, 2026
@fgogolli

Copy link
Copy Markdown
Contributor

Superseded by #256. #257 and #258. Will close this one @IkeM-L

@fgogolli fgogolli closed this Jun 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants