Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion gateway/api/management/commands/sync_ce_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"project_name",
"region",
"resource_group_id",
"subnet_pool_id",
"pds_name_state",
"pds_name_users",
"pds_name_providers",
Expand All @@ -34,6 +33,48 @@
]


def _resolve_subnet_pool_defaults(project_id: str, data: dict) -> dict | None:
"""Build the subnet-pool fields for the upsert defaults.

Config supplies either ``subnet_pool_id`` directly, or ``subnet_pool_name``
(with the id resolved and cached at submit time by the fleets runner). At least
one must be present.

When only a name is given, the cached ``subnet_pool_id`` on an existing row must
survive re-sync — ``update_or_create`` overwrites every default on every boot, so
a blank id in the defaults would wipe the cache each boot and force a needless
re-resolution. The id is therefore only blanked when the configured name differs
from the stored one (a rename that must invalidate the cache).

Args:
project_id: The CE project UUID.
data: The config entry.

Returns:
A dict of subnet-pool fields to merge into the upsert defaults, or ``None``
when neither id nor name is configured.
"""
subnet_pool_id = data.get("subnet_pool_id")
subnet_pool_name = data.get("subnet_pool_name")

if not subnet_pool_id and not subnet_pool_name:
logger.error("project_id=%s missing both subnet_pool_id and subnet_pool_name", project_id)
return None

defaults = {"subnet_pool_name": subnet_pool_name or None}

if subnet_pool_id:
# Explicit id in config wins and is written through.
defaults["subnet_pool_id"] = subnet_pool_id
return defaults

# Name-only config: keep any cached id unless the name changed (rename → invalidate).
existing = CodeEngineProject.objects.filter(project_id=project_id).values("subnet_pool_name").first()
if existing is not None and existing["subnet_pool_name"] != subnet_pool_name:
defaults["subnet_pool_id"] = None
return defaults


def _upsert_project(project_id: str, data: dict) -> bool:
"""Create or update a single CodeEngineProject row.

Expand All @@ -49,7 +90,12 @@ def _upsert_project(project_id: str, data: dict) -> bool:
logger.error("project_id=%s missing required fields: %s", project_id, ", ".join(missing))
return False

subnet_defaults = _resolve_subnet_pool_defaults(project_id, data)
if subnet_defaults is None:
return False

defaults = {k: data[k] for k in _REQUIRED_KEYS}
defaults.update(subnet_defaults)
defaults["active"] = True

_, created = CodeEngineProject.objects.update_or_create(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Generated by Django 6.0.8 on 2026-09-02 15:06

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("api", "0061_job_filler"),
]

operations = [
migrations.AddField(
model_name="codeengineproject",
name="subnet_pool_name",
field=models.CharField(
blank=True,
help_text="Subnet pool name for fleet networking. When set and subnet_pool_id is empty, the id is resolved from this name on first job submission and cached into subnet_pool_id.",
max_length=255,
null=True,
),
),
migrations.AlterField(
model_name="codeengineproject",
name="subnet_pool_id",
field=models.CharField(
blank=True,
help_text="Subnet pool ID for fleet networking. May be supplied directly in config, or left empty and resolved from subnet_pool_name (then cached here).",
max_length=255,
null=True,
),
),
]
3 changes: 3 additions & 0 deletions gateway/core/ibm_cloud/code_engine/ce_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

from core.ibm_cloud.code_engine.ce_client.api.fleets_api import FleetsApi
from core.ibm_cloud.code_engine.ce_client.api.secrets_and_configmaps_api import SecretsAndConfigmapsApi
from core.ibm_cloud.code_engine.ce_client.api.subnet_pools_api import SubnetPoolsApi

from core.ibm_cloud.code_engine.ce_client.api_client import ApiClient
from core.ibm_cloud.code_engine.ce_client.configuration import Configuration
Expand All @@ -45,3 +46,5 @@
from core.ibm_cloud.code_engine.ce_client.models.v2_network_placement import V2NetworkPlacement
from core.ibm_cloud.code_engine.ce_client.models.v2_secret import V2Secret
from core.ibm_cloud.code_engine.ce_client.models.v2_secret_list import V2SecretList
from core.ibm_cloud.code_engine.ce_client.models.v2_subnet_pool import V2SubnetPool
from core.ibm_cloud.code_engine.ce_client.models.v2_subnet_pool_list import V2SubnetPoolList
150 changes: 150 additions & 0 deletions gateway/core/ibm_cloud/code_engine/ce_client/api/subnet_pools_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# This code is a Qiskit project.
#
# (C) Copyright IBM 2026.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

# coding: utf-8

"""
Code Engine

REST API for Code Engine # noqa: E501

OpenAPI spec version: 2.0.0

Generated by: https://github.com/swagger-api/swagger-codegen.git
"""

from __future__ import absolute_import

import re # noqa: F401

# python 2 and python 3 compatibility library
import six

from core.ibm_cloud.code_engine.ce_client.api_client import ApiClient


class SubnetPoolsApi(object):
"""NOTE: This class is auto generated by the swagger code generator program.

Do not edit the class manually.
Ref: https://github.com/swagger-api/swagger-codegen

Only ``list_subnet_pools`` is vendored: the gateway resolves a configured
subnet-pool name to its id and never creates, gets by id, or deletes pools.
"""

def __init__(self, api_client=None):
if api_client is None:
api_client = ApiClient()
self.api_client = api_client

def list_subnet_pools(self, project_id, **kwargs): # noqa: E501
"""List subnet pools # noqa: E501

List all subnet pools in a project. # noqa: E501
This method makes a synchronous HTTP request by default. To make an
asynchronous HTTP request, please pass async_req=True
>>> thread = api.list_subnet_pools(project_id, async_req=True)
>>> result = thread.get()

:param async_req bool
:param str project_id: The ID of the project. (required)
:param int limit: Optional maximum number of subnet pools per page.
:param str start: An optional token that indicates the beginning of the page of results to be returned. If omitted, the first page of results is returned. This value is obtained from the 'start' query parameter in the `next` object of the operation response.
:return: V2SubnetPoolList
If the method is called asynchronously,
returns the request thread.
"""
kwargs["_return_http_data_only"] = True
if kwargs.get("async_req"):
return self.list_subnet_pools_with_http_info(project_id, **kwargs) # noqa: E501
else:
data = self.list_subnet_pools_with_http_info(project_id, **kwargs) # noqa: E501
return data

def list_subnet_pools_with_http_info(self, project_id, **kwargs): # noqa: E501
"""List subnet pools # noqa: E501

List all subnet pools in a project. # noqa: E501
This method makes a synchronous HTTP request by default. To make an
asynchronous HTTP request, please pass async_req=True
>>> thread = api.list_subnet_pools_with_http_info(project_id, async_req=True)
>>> result = thread.get()

:param async_req bool
:param str project_id: The ID of the project. (required)
:param int limit: Optional maximum number of subnet pools per page.
:param str start: An optional token that indicates the beginning of the page of results to be returned. If omitted, the first page of results is returned. This value is obtained from the 'start' query parameter in the `next` object of the operation response.
:return: V2SubnetPoolList
If the method is called asynchronously,
returns the request thread.
"""

all_params = ["project_id", "limit", "start"] # noqa: E501
all_params.append("async_req")
all_params.append("_return_http_data_only")
all_params.append("_preload_content")
all_params.append("_request_timeout")

params = locals()
for key, val in six.iteritems(params["kwargs"]):
if key not in all_params:
raise TypeError("Got an unexpected keyword argument '%s'" " to method list_subnet_pools" % key)
params[key] = val
del params["kwargs"]
# verify the required parameter 'project_id' is set
if "project_id" not in params or params["project_id"] is None:
raise ValueError(
"Missing the required parameter `project_id` when calling `list_subnet_pools`"
) # noqa: E501

collection_formats = {}

path_params = {}
if "project_id" in params:
path_params["project_id"] = params["project_id"] # noqa: E501

query_params = []
if "limit" in params:
query_params.append(("limit", params["limit"])) # noqa: E501
if "start" in params:
query_params.append(("start", params["start"])) # noqa: E501

header_params = {}

form_params = []
local_var_files = {}

body_params = None
# HTTP header `Accept`
header_params["Accept"] = self.api_client.select_header_accept(["application/json"]) # noqa: E501

# Authentication setting
auth_settings = ["Bearer"] # noqa: E501

return self.api_client.call_api(
"/projects/{project_id}/subnet_pools",
"GET",
path_params,
query_params,
header_params,
body=body_params,
post_params=form_params,
files=local_var_files,
response_type="V2SubnetPoolList", # noqa: E501
auth_settings=auth_settings,
async_req=params.get("async_req"),
_return_http_data_only=params.get("_return_http_data_only"),
_preload_content=params.get("_preload_content", True),
_request_timeout=params.get("_request_timeout"),
collection_formats=collection_formats,
)
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@
from core.ibm_cloud.code_engine.ce_client.models.v2_network_placement import V2NetworkPlacement
from core.ibm_cloud.code_engine.ce_client.models.v2_secret import V2Secret
from core.ibm_cloud.code_engine.ce_client.models.v2_secret_list import V2SecretList
from core.ibm_cloud.code_engine.ce_client.models.v2_subnet_pool import V2SubnetPool
from core.ibm_cloud.code_engine.ce_client.models.v2_subnet_pool_list import V2SubnetPoolList
Loading