Skip to content

Commit 659ba3f

Browse files
vertex-sdk-botcopybara-github
authored andcommitted
feat: support creating optimized online store with private service connect
PiperOrigin-RevId: 662289750
1 parent c68d559 commit 659ba3f

File tree

3 files changed

+155
-42
lines changed

3 files changed

+155
-42
lines changed

tests/unit/vertexai/feature_store_constants.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,12 @@
100100
_TEST_PSC_OPTIMIZED_FOS = types.feature_online_store_v1.FeatureOnlineStore(
101101
name=_TEST_PSC_OPTIMIZED_FOS_PATH,
102102
optimized=types.feature_online_store_v1.FeatureOnlineStore.Optimized(),
103-
dedicated_serving_endpoint=types.feature_online_store_v1.FeatureOnlineStore.DedicatedServingEndpoint(),
103+
dedicated_serving_endpoint=types.feature_online_store_v1.FeatureOnlineStore.DedicatedServingEndpoint(
104+
private_service_connect_config=types.service_networking_v1.PrivateServiceConnectConfig(
105+
enable_private_service_connect=True,
106+
project_allowlist=_TEST_PSC_PROJECT_ALLOWLIST,
107+
),
108+
),
104109
labels=_TEST_PSC_OPTIMIZED_FOS_LABELS,
105110
)
106111

tests/unit/vertexai/test_feature_online_store.py

+121-36
Original file line numberDiff line numberDiff line change
@@ -15,61 +15,61 @@
1515
# limitations under the License.
1616
#
1717

18-
from unittest.mock import call
1918
import re
19+
from typing import Dict
2020
from unittest import mock
21+
from unittest.mock import call
2122
from unittest.mock import patch
22-
from typing import Dict
2323

2424
from google.api_core import operation as ga_operation
2525
from google.cloud import aiplatform
2626
from google.cloud.aiplatform import base
2727
from google.cloud.aiplatform.compat import types
28-
from vertexai.resources.preview import (
29-
FeatureOnlineStore,
30-
FeatureOnlineStoreType,
31-
FeatureViewBigQuerySource,
32-
IndexConfig,
33-
DistanceMeasureType,
34-
TreeAhConfig,
35-
)
36-
from vertexai.resources.preview.feature_store import (
37-
feature_online_store,
38-
)
3928
from google.cloud.aiplatform.compat.services import (
4029
feature_online_store_admin_service_client,
4130
)
42-
import pytest
43-
44-
from test_feature_view import fv_eq
4531
from feature_store_constants import (
46-
_TEST_PROJECT,
47-
_TEST_LOCATION,
48-
_TEST_PARENT,
4932
_TEST_BIGTABLE_FOS1_ID,
50-
_TEST_BIGTABLE_FOS1_PATH,
5133
_TEST_BIGTABLE_FOS1_LABELS,
34+
_TEST_BIGTABLE_FOS1_PATH,
5235
_TEST_BIGTABLE_FOS2_ID,
53-
_TEST_BIGTABLE_FOS2_PATH,
5436
_TEST_BIGTABLE_FOS2_LABELS,
37+
_TEST_BIGTABLE_FOS2_PATH,
5538
_TEST_BIGTABLE_FOS3_ID,
56-
_TEST_BIGTABLE_FOS3_PATH,
5739
_TEST_BIGTABLE_FOS3_LABELS,
40+
_TEST_BIGTABLE_FOS3_PATH,
5841
_TEST_ESF_OPTIMIZED_FOS_ID,
59-
_TEST_ESF_OPTIMIZED_FOS_PATH,
6042
_TEST_ESF_OPTIMIZED_FOS_LABELS,
61-
_TEST_PSC_OPTIMIZED_FOS_ID,
62-
_TEST_PSC_OPTIMIZED_FOS_LABELS,
63-
_TEST_PSC_PROJECT_ALLOWLIST,
43+
_TEST_ESF_OPTIMIZED_FOS_PATH,
6444
_TEST_FOS_LIST,
65-
_TEST_FV1_ID,
66-
_TEST_FV1_PATH,
67-
_TEST_FV1_LABELS,
6845
_TEST_FV1_BQ_URI,
6946
_TEST_FV1_ENTITY_ID_COLUMNS,
47+
_TEST_FV1_ID,
48+
_TEST_FV1_LABELS,
49+
_TEST_FV1_PATH,
50+
_TEST_LOCATION,
7051
_TEST_OPTIMIZED_EMBEDDING_FV_ID,
7152
_TEST_OPTIMIZED_EMBEDDING_FV_PATH,
53+
_TEST_PARENT,
54+
_TEST_PROJECT,
55+
_TEST_PSC_OPTIMIZED_FOS_ID,
56+
_TEST_PSC_OPTIMIZED_FOS_LABELS,
57+
_TEST_PSC_OPTIMIZED_FOS_PATH,
58+
_TEST_PSC_PROJECT_ALLOWLIST,
7259
)
60+
from test_feature_view import fv_eq
61+
from vertexai.resources.preview import (
62+
DistanceMeasureType,
63+
FeatureOnlineStore,
64+
FeatureOnlineStoreType,
65+
FeatureViewBigQuerySource,
66+
IndexConfig,
67+
TreeAhConfig,
68+
)
69+
from vertexai.resources.preview.feature_store import (
70+
feature_online_store,
71+
)
72+
import pytest
7373

7474

7575
@pytest.fixture
@@ -277,24 +277,109 @@ def test_create_esf_optimized_store(
277277
)
278278

279279

280-
@pytest.mark.parametrize("create_request_timeout", [None, 1.0])
281-
def test_create_psc_optimized_store(
282-
create_request_timeout,
283-
):
280+
def test_create_psc_optimized_store_no_project_allowlist_raises_error():
284281
aiplatform.init(project=_TEST_PROJECT, location=_TEST_LOCATION)
282+
285283
with pytest.raises(
286284
ValueError,
287-
match=re.escape("private_service_connect is not supported"),
285+
match=re.escape(
286+
"`project_allowlist` cannot be empty when `enable_private_service_connect` is"
287+
" set to true."
288+
),
288289
):
289290
FeatureOnlineStore.create_optimized_store(
290291
_TEST_PSC_OPTIMIZED_FOS_ID,
291292
labels=_TEST_PSC_OPTIMIZED_FOS_LABELS,
292-
create_request_timeout=create_request_timeout,
293293
enable_private_service_connect=True,
294-
project_allowlist=_TEST_PSC_PROJECT_ALLOWLIST,
295294
)
296295

297296

297+
def test_create_psc_optimized_store_empty_project_allowlist_raises_error():
298+
aiplatform.init(project=_TEST_PROJECT, location=_TEST_LOCATION)
299+
300+
with pytest.raises(
301+
ValueError,
302+
match=re.escape(
303+
"`project_allowlist` cannot be empty when `enable_private_service_connect` is"
304+
" set to true."
305+
),
306+
):
307+
FeatureOnlineStore.create_optimized_store(
308+
_TEST_PSC_OPTIMIZED_FOS_ID,
309+
enable_private_service_connect=True,
310+
project_allowlist=[],
311+
)
312+
313+
314+
@pytest.mark.parametrize("create_request_timeout", [None, 1.0])
315+
@pytest.mark.parametrize("sync", [True, False])
316+
def test_create_psc_optimized_store(
317+
create_psc_optimized_fos_mock,
318+
get_psc_optimized_fos_mock,
319+
fos_logger_mock,
320+
create_request_timeout,
321+
sync,
322+
):
323+
aiplatform.init(project=_TEST_PROJECT, location=_TEST_LOCATION)
324+
fos = FeatureOnlineStore.create_optimized_store(
325+
_TEST_PSC_OPTIMIZED_FOS_ID,
326+
labels=_TEST_PSC_OPTIMIZED_FOS_LABELS,
327+
create_request_timeout=create_request_timeout,
328+
enable_private_service_connect=True,
329+
project_allowlist=_TEST_PSC_PROJECT_ALLOWLIST,
330+
)
331+
332+
if not sync:
333+
fos.wait()
334+
335+
expected_feature_online_store = types.feature_online_store_v1.FeatureOnlineStore(
336+
optimized=types.feature_online_store_v1.FeatureOnlineStore.Optimized(),
337+
dedicated_serving_endpoint=types.feature_online_store_v1.FeatureOnlineStore.DedicatedServingEndpoint(
338+
private_service_connect_config=types.service_networking_v1.PrivateServiceConnectConfig(
339+
enable_private_service_connect=True,
340+
project_allowlist=_TEST_PSC_PROJECT_ALLOWLIST,
341+
)
342+
),
343+
labels=_TEST_PSC_OPTIMIZED_FOS_LABELS,
344+
)
345+
create_psc_optimized_fos_mock.assert_called_once_with(
346+
parent=_TEST_PARENT,
347+
feature_online_store=expected_feature_online_store,
348+
feature_online_store_id=_TEST_PSC_OPTIMIZED_FOS_ID,
349+
metadata=(),
350+
timeout=create_request_timeout,
351+
)
352+
353+
fos_logger_mock.assert_has_calls(
354+
[
355+
call("Creating FeatureOnlineStore"),
356+
call(
357+
"Create FeatureOnlineStore backing LRO:"
358+
f" {create_psc_optimized_fos_mock.return_value.operation.name}"
359+
),
360+
call(
361+
"FeatureOnlineStore created. Resource name:"
362+
" projects/test-project/locations/us-central1/featureOnlineStores/my_psc_optimized_fos"
363+
),
364+
call("To use this FeatureOnlineStore in another session:"),
365+
call(
366+
"feature_online_store ="
367+
" aiplatform.FeatureOnlineStore('projects/test-project/locations/us-central1/featureOnlineStores/my_psc_optimized_fos')"
368+
),
369+
]
370+
)
371+
372+
fos_eq(
373+
fos,
374+
name=_TEST_PSC_OPTIMIZED_FOS_ID,
375+
resource_name=_TEST_PSC_OPTIMIZED_FOS_PATH,
376+
project=_TEST_PROJECT,
377+
location=_TEST_LOCATION,
378+
labels=_TEST_PSC_OPTIMIZED_FOS_LABELS,
379+
type=FeatureOnlineStoreType.OPTIMIZED,
380+
)
381+
382+
298383
def test_list(list_fos_mock):
299384
aiplatform.init(project=_TEST_PROJECT, location=_TEST_LOCATION)
300385

vertexai/resources/preview/feature_store/feature_online_store.py

+28-5
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
)
3232
from google.cloud.aiplatform.compat.types import (
3333
feature_online_store as gca_feature_online_store,
34+
service_networking as gca_service_networking,
3435
feature_view as gca_feature_view,
3536
)
3637
from vertexai.resources.preview.feature_store.feature_view import (
@@ -245,18 +246,30 @@ def create_optimized_store(
245246
246247
Example Usage:
247248
249+
```
250+
# Create optimized store with public endpoint.
248251
my_fos = vertexai.preview.FeatureOnlineStore.create_optimized_store('my_fos')
252+
```
253+
254+
```
255+
# Create optimized online store with private service connect.
256+
my_fos = vertexai.preview.FeatureOnlineStore.create_optimized_store(
257+
'my_fos',
258+
enable_private_service_connect=True,
259+
project_allowlist=['my-project'],
260+
)
261+
```
249262
250263
Args:
251264
name: The name of the feature online store.
252-
enable_private_service_connect (bool):
265+
enable_private_service_connect:
253266
Optional. If true, expose the optimized online store
254267
via private service connect. Otherwise the optimized online
255-
store will be accessible through public endpoint
256-
project_allowlist (MutableSequence[str]):
268+
store will be accessible through public endpoint.
269+
project_allowlist:
257270
A list of Projects from which the forwarding
258271
rule will target the service attachment. Only needed when
259-
enable_private_service_connect is set to true.
272+
`enable_private_service_connect` is set to true.
260273
labels:
261274
The labels with user-defined metadata to organize your feature
262275
online store. Label keys and values can be no longer than 64
@@ -290,7 +303,17 @@ def create_optimized_store(
290303
FeatureOnlineStore - the FeatureOnlineStore resource object.
291304
"""
292305
if enable_private_service_connect:
293-
raise ValueError("private_service_connect is not supported")
306+
if not project_allowlist:
307+
raise ValueError(
308+
"`project_allowlist` cannot be empty when `enable_private_service_connect` is set to true."
309+
)
310+
311+
dedicated_serving_endpoint = gca_feature_online_store.FeatureOnlineStore.DedicatedServingEndpoint(
312+
private_service_connect_config=gca_service_networking.PrivateServiceConnectConfig(
313+
enable_private_service_connect=True,
314+
project_allowlist=project_allowlist,
315+
),
316+
)
294317
else:
295318
dedicated_serving_endpoint = (
296319
gca_feature_online_store.FeatureOnlineStore.DedicatedServingEndpoint()

0 commit comments

Comments
 (0)