Skip to content

Commit 0e344aa

Browse files
Merge branch 'kubeflow:main' into main
2 parents 5efb1c6 + 8f25d29 commit 0e344aa

Some content is hidden

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

59 files changed

+1985
-943
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Build the model-registry binary
2-
FROM --platform=$BUILDPLATFORM registry.access.redhat.com/ubi8/go-toolset:1.21 AS builder
2+
FROM --platform=$BUILDPLATFORM registry.access.redhat.com/ubi8/go-toolset:1.22 AS builder
33
ARG TARGETOS
44
ARG TARGETARCH
55

Dockerfile.odh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Build the model-registry binary
2-
FROM registry.access.redhat.com/ubi8/go-toolset:1.21 AS builder
2+
FROM registry.access.redhat.com/ubi8/go-toolset:1.22 AS builder
33

44
WORKDIR /workspace
55
# Copy the Go Modules manifests

Makefile

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ GO ?= "$(shell which go)"
66
BFF_PATH := $(PROJECT_PATH)/clients/ui/bff
77
UI_PATH := $(PROJECT_PATH)/clients/ui/frontend
88

9+
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
10+
ENVTEST_K8S_VERSION = 1.29
11+
ENVTEST ?= $(PROJECT_BIN)/setup-envtest
12+
913
# add tools bin directory
1014
PATH := $(PROJECT_BIN):$(PATH)
1115

@@ -133,6 +137,9 @@ bin/protoc-gen-go:
133137
bin/protoc-gen-go-grpc:
134138
GOBIN=$(PROJECT_BIN) ${GO} install google.golang.org/grpc/cmd/[email protected]
135139

140+
bin/envtest:
141+
GOBIN=$(PROJECT_BIN) ${GO} install sigs.k8s.io/controller-runtime/tools/[email protected]
142+
136143
GOLANGCI_LINT ?= ${PROJECT_BIN}/golangci-lint
137144
bin/golangci-lint:
138145
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(PROJECT_BIN) v1.61.0
@@ -164,7 +171,7 @@ clean/deps:
164171
rm -Rf bin/*
165172

166173
.PHONY: deps
167-
deps: bin/protoc bin/go-enum bin/protoc-gen-go bin/protoc-gen-go-grpc bin/golangci-lint bin/goverter bin/openapi-generator-cli
174+
deps: bin/protoc bin/go-enum bin/protoc-gen-go bin/protoc-gen-go-grpc bin/golangci-lint bin/goverter bin/openapi-generator-cli bin/envtest
168175

169176
.PHONY: vendor
170177
vendor:
@@ -196,20 +203,20 @@ gen: deps gen/grpc gen/openapi gen/openapi-server gen/converter
196203

197204
.PHONY: lint
198205
lint:
199-
${GOLANGCI_LINT} run main.go
200-
${GOLANGCI_LINT} run cmd/... internal/... ./pkg/...
206+
${GOLANGCI_LINT} run main.go --timeout 3m
207+
${GOLANGCI_LINT} run cmd/... internal/... ./pkg/... --timeout 3m
201208

202209
.PHONY: test
203-
test: gen
204-
${GO} test ./internal/... ./pkg/...
210+
test: gen bin/envtest
211+
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" ${GO} test ./internal/... ./pkg/...
205212

206213
.PHONY: test-nocache
207-
test-nocache: gen
208-
${GO} test ./internal/... ./pkg/... -count=1
214+
test-nocache: gen bin/envtest
215+
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" ${GO} test ./internal/... ./pkg/... -count=1
209216

210217
.PHONY: test-cover
211-
test-cover: gen
212-
${GO} test ./internal/... ./pkg/... -coverprofile=coverage.txt
218+
test-cover: gen bin/envtest
219+
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" ${GO} test ./internal/... ./pkg/... -coverprofile=coverage.txt
213220
${GO} tool cover -html=coverage.txt -o coverage.html
214221

215222
.PHONY: run/proxy
@@ -249,12 +256,12 @@ ifeq ($(DOCKER),docker)
249256
# docker uses builder containers
250257
- $(DOCKER) buildx rm model-registry-builder
251258
$(DOCKER) buildx create --use --name model-registry-builder --platform=$(PLATFORMS)
252-
$(DOCKER) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f ${DOCKERFILE} .
259+
$(DOCKER) buildx build --push --platform=$(PLATFORMS) --tag ${IMG}:$(IMG_VERSION) -f ${DOCKERFILE} .
253260
$(DOCKER) buildx rm model-registry-builder
254261
else ifeq ($(DOCKER),podman)
255262
# podman uses image manifests
256263
$(DOCKER) manifest create -a ${IMG}
257-
$(DOCKER) buildx build --platform=$(PLATFORMS) --manifest ${IMG} -f ${DOCKERFILE} .
264+
$(DOCKER) buildx build --platform=$(PLATFORMS) --manifest ${IMG}:$(IMG_VERSION) -f ${DOCKERFILE} .
258265
$(DOCKER) manifest push ${IMG}
259266
$(DOCKER) manifest rm ${IMG}
260267
else

clients/python/poetry.lock

Lines changed: 468 additions & 376 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

clients/python/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ sphinx-autobuild = ">=2021.3.14,<2025.0.0"
4444
pytest = ">=7.4.2,<9.0.0"
4545
coverage = { extras = ["toml"], version = "^7.3.2" }
4646
pytest-cov = ">=4.1,<7.0"
47-
ruff = ">=0.5.2,<0.8.0"
47+
ruff = ">=0.5.2,<0.9.0"
4848
mypy = "^1.7.0"
4949
pytest-asyncio = ">=0.23.7,<0.25.0"
5050
requests = "^2.32.2"

clients/python/src/model_registry/_client.py

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
import logging
56
import os
67
from collections.abc import Mapping
78
from pathlib import Path
@@ -22,6 +23,30 @@
2223
ModelTypes = Union[RegisteredModel, ModelVersion, ModelArtifact]
2324
TModel = TypeVar("TModel", bound=ModelTypes)
2425

26+
logging.basicConfig(
27+
format="%(asctime)s.%(msecs)03d - %(name)s:%(levelname)s: %(message)s",
28+
datefmt="%H:%M:%S",
29+
level=logging.WARNING, # the default loglevel
30+
handlers=[
31+
# logging.FileHandler(
32+
# LOGS
33+
# / "log-{}-{}.log".format(
34+
# datetime.now(tz=datetime.now().astimezone().tzinfo).strftime(
35+
# "%Y-%m-%d-%H-%M-%S"
36+
# ),
37+
# os.getpid(),
38+
# ),
39+
# encoding="utf-8",
40+
# delay=False,
41+
# ),
42+
logging.StreamHandler(),
43+
],
44+
)
45+
46+
logger = logging.getLogger("model-registry")
47+
48+
DEFAULT_USER_TOKEN_ENVVAR = "KF_PIPELINES_SA_TOKEN_PATH" # noqa: S105
49+
2550

2651
class ModelRegistry:
2752
"""Model registry client."""
@@ -34,7 +59,10 @@ def __init__(
3459
author: str,
3560
is_secure: bool = True,
3661
user_token: str | None = None,
62+
user_token_envvar: str = DEFAULT_USER_TOKEN_ENVVAR,
3763
custom_ca: str | None = None,
64+
custom_ca_envvar: str | None = None,
65+
log_level: int = logging.WARNING,
3866
):
3967
"""Constructor.
4068
@@ -45,47 +73,59 @@ def __init__(
4573
Keyword Args:
4674
author: Name of the author.
4775
is_secure: Whether to use a secure connection. Defaults to True.
48-
user_token: The PEM-encoded user token as a string. Defaults to content of path on envvar KF_PIPELINES_SA_TOKEN_PATH.
49-
custom_ca: Path to the PEM-encoded root certificates as a string. Defaults to path on envvar CERT.
76+
user_token: The PEM-encoded user token as a string.
77+
user_token_envvar: Environment variable to read the user token from if it's not passed as an arg. Defaults to KF_PIPELINES_SA_TOKEN_PATH.
78+
custom_ca: Path to the PEM-encoded root certificates as a string.
79+
custom_ca_envvar: Environment variable to read the custom CA from if it's not passed as an arg.
80+
log_level: Log level. Defaults to logging.WARNING.
5081
"""
82+
logger.setLevel(log_level)
83+
5184
import nest_asyncio
5285

86+
logger.debug("Setting up reentrant async event loop")
5387
nest_asyncio.apply()
5488

5589
# TODO: get remaining args from env
5690
self._author = author
5791

58-
if not user_token:
92+
if not user_token and user_token_envvar:
93+
logger.info("Reading user token from %s", user_token_envvar)
5994
# /var/run/secrets/kubernetes.io/serviceaccount/token
60-
sa_token = os.environ.get("KF_PIPELINES_SA_TOKEN_PATH")
61-
if sa_token:
95+
if sa_token := os.environ.get(user_token_envvar):
96+
if user_token_envvar == DEFAULT_USER_TOKEN_ENVVAR:
97+
logger.warning(
98+
f"Sourcing user token from default envvar: {DEFAULT_USER_TOKEN_ENVVAR}"
99+
)
62100
user_token = Path(sa_token).read_text()
63101
else:
64102
warn("User access token is missing", stacklevel=2)
65103

66104
if is_secure:
67-
root_ca = None
68-
if not custom_ca:
69-
if cert := os.getenv("CERT"):
70-
root_ca = cert
71-
# client might have a default CA setup
72-
else:
73-
root_ca = custom_ca
105+
if (
106+
not custom_ca
107+
and custom_ca_envvar
108+
and (cert := os.getenv(custom_ca_envvar))
109+
):
110+
logger.info(
111+
"Using custom CA envvar %s",
112+
custom_ca_envvar,
113+
)
114+
custom_ca = cert
115+
# client might have a default CA setup
74116

75117
if not user_token:
76118
msg = "user token must be provided for secure connection"
77119
raise StoreError(msg)
78120

79121
self._api = ModelRegistryAPIClient.secure_connection(
80-
server_address, port, user_token=user_token, custom_ca=root_ca
122+
server_address, port, user_token=user_token, custom_ca=custom_ca
81123
)
82-
elif custom_ca:
83-
msg = "Custom CA provided without secure connection, conflicting options"
84-
raise StoreError(msg)
85124
else:
86125
self._api = ModelRegistryAPIClient.insecure_connection(
87126
server_address, port, user_token
88127
)
128+
self.get_registered_models().page_size(1)._next_page()
89129

90130
def async_runner(self, coro: Any) -> Any:
91131
import asyncio

clients/ui/.env

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

22
############### Default settings ###############
33
CONTAINER_TOOL=docker
4-
IMG_BFF=kubeflow/model-registry-bff:dev-latest
5-
IMG_FRONTEND=kubeflow/model-registry-ui:dev-latest
4+
IMG_BFF=kubeflow/model-registry-bff:latest
5+
IMG_FRONTEND=kubeflow/model-registry-ui:latest

clients/ui/OWNERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
approvers:
22
- ederign
33
- alexcreasy
4+
- lucferbux
5+
- Griffin-Sullivan

clients/ui/bff/README.md

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -71,21 +71,23 @@ make docker-build
7171
| POST /api/v1/model_registry/{model_registry_id}/model_versions/{model_version_id}/artifacts | CreateModelArtifactByModelVersion | Create a ModelArtifact entity for a specific ModelVersion |
7272

7373
### Sample local calls
74+
75+
You will need to inject your requests with a kubeflow-userid header for authorization purposes. When running the service with the mocked Kubernetes client (MOCK_K8S_CLIENT=true), the user [email protected] is preconfigured with the necessary RBAC permissions to perform these actions.
7476
```
7577
# GET /v1/healthcheck
76-
curl -i localhost:4000/api/v1/healthcheck
78+
curl -i -H "kubeflow-userid: [email protected]" localhost:4000/api/v1/healthcheck
7779
```
7880
```
7981
# GET /v1/model_registry
80-
curl -i localhost:4000/api/v1/model_registry
82+
curl -i -H "kubeflow-userid: [email protected]" localhost:4000/api/v1/model_registry
8183
```
8284
```
8385
# GET /v1/model_registry/{model_registry_id}/registered_models
84-
curl -i localhost:4000/api/v1/model_registry/model-registry/registered_models
86+
curl -i -H "kubeflow-userid: [email protected]" localhost:4000/api/v1/model_registry/model-registry/registered_models
8587
```
8688
```
8789
#POST /v1/model_registry/{model_registry_id}/registered_models
88-
curl -i -X POST "http://localhost:4000/api/v1/model_registry/model-registry/registered_models" \
90+
curl -i -H "kubeflow-userid: [email protected]" -X POST "http://localhost:4000/api/v1/model_registry/model-registry/registered_models" \
8991
-H "Content-Type: application/json" \
9092
-d '{ "data": {
9193
"customProperties": {
@@ -103,23 +105,23 @@ curl -i -X POST "http://localhost:4000/api/v1/model_registry/model-registry/regi
103105
```
104106
```
105107
# GET /v1/model_registry/{model_registry_id}/registered_models/{registered_model_id}
106-
curl -i localhost:4000/api/v1/model_registry/model-registry/registered_models/1
108+
curl -i -H "kubeflow-userid: [email protected]" localhost:4000/api/v1/model_registry/model-registry/registered_models/1
107109
```
108110
```
109111
# PATCH /v1/model_registry/{model_registry_id}/registered_models/{registered_model_id}
110-
curl -i -X PATCH "http://localhost:4000/api/v1/model_registry/model-registry/registered_models/1" \
112+
curl -i -H "kubeflow-userid: [email protected]" -X PATCH "http://localhost:4000/api/v1/model_registry/model-registry/registered_models/1" \
111113
-H "Content-Type: application/json" \
112114
-d '{ "data": {
113115
"description": "New description"
114116
}}'
115117
```
116118
```
117119
# GET /api/v1/model_registry/{model_registry_id}/model_versions/{model_version_id}
118-
curl -i http://localhost:4000/api/v1/model_registry/model-registry/model_versions/1
120+
curl -i -H "kubeflow-userid: [email protected]" http://localhost:4000/api/v1/model_registry/model-registry/model_versions/1
119121
```
120122
```
121123
# POST /api/v1/model_registry/{model_registry_id}/model_versions
122-
curl -i -X POST "http://localhost:4000/api/v1/model_registry/model-registry/model_versions" \
124+
curl -i -H "kubeflow-userid: [email protected]" -X POST "http://localhost:4000/api/v1/model_registry/model-registry/model_versions" \
123125
-H "Content-Type: application/json" \
124126
-d '{ "data": {
125127
"customProperties": {
@@ -138,19 +140,19 @@ curl -i -X POST "http://localhost:4000/api/v1/model_registry/model-registry/mode
138140
```
139141
```
140142
# PATCH /api/v1/model_registry/{model_registry_id}/model_versions/{model_version_id}
141-
curl -i -X PATCH "http://localhost:4000/api/v1/model_registry/model-registry/model_versions/1" \
143+
curl -i -H "kubeflow-userid: [email protected]" -X PATCH "http://localhost:4000/api/v1/model_registry/model-registry/model_versions/1" \
142144
-H "Content-Type: application/json" \
143145
-d '{ "data": {
144146
"description": "New description 2"
145147
}}'
146148
```
147149
```
148150
# GET /v1/model_registry/{model_registry_id}/registered_models/{registered_model_id}/versions
149-
curl -i localhost:4000/api/v1/model_registry/model-registry/registered_models/1/versions
151+
curl -i -H "kubeflow-userid: [email protected]" localhost:4000/api/v1/model_registry/model-registry/registered_models/1/versions
150152
```
151153
```
152154
# POST /v1/model_registry/{model_registry_id}/registered_models/{registered_model_id}/versions
153-
curl -i -X POST "http://localhost:4000/api/v1/model_registry/model-registry/registered_models/1/versions" \
155+
curl -i -H "kubeflow-userid: [email protected]" -X POST "http://localhost:4000/api/v1/model_registry/model-registry/registered_models/1/versions" \
154156
-H "Content-Type: application/json" \
155157
-d '{ "data": {
156158
"customProperties": {
@@ -163,17 +165,17 @@ curl -i -X POST "http://localhost:4000/api/v1/model_registry/model-registry/regi
163165
"externalId": "9928",
164166
"name": "ModelVersion One",
165167
"state": "LIVE",
166-
"author": "alex"
168+
"author": "alex",
167169
"registeredModelId: "1"
168170
}}'
169171
```
170172
```
171173
# GET /api/v1/model_registry/{model_registry_id}/model_versions/{model_version_id}/artifacts
172-
curl -i http://localhost:4000/api/v1/model_registry/model-registry/model_versions/1/artifacts
174+
curl -i -H "kubeflow-userid: [email protected]" http://localhost:4000/api/v1/model_registry/model-registry/model_versions/1/artifacts
173175
```
174176
```
175177
# POST /api/v1/model_registry/{model_registry_id}/model_versions/{model_version_id}/artifacts
176-
curl -i -X POST "http://localhost:4000/api/v1/model_registry/model-registry/model_versions/1/artifacts" \
178+
curl -i -H "kubeflow-userid: [email protected]" -X POST "http://localhost:4000/api/v1/model_registry/model-registry/model_versions/1/artifacts" \
177179
-H "Content-Type: application/json" \
178180
-d '{ "data": {
179181
"customProperties": {
@@ -203,9 +205,9 @@ The following query parameters are supported by "Get All" style endpoints to con
203205
### Sample local calls
204206
```
205207
# Get with a page size of 5 getting a specific page.
206-
curl -i "http://localhost:4000/api/v1/model_registry/model-registry/registered_models?pageSize=5&nextPageToken=CAEQARoCCAE"
208+
curl -i -H "kubeflow-userid: [email protected]" "http://localhost:4000/api/v1/model_registry/model-registry/registered_models?pageSize=5&nextPageToken=CAEQARoCCAE"
207209
```
208210
```
209211
# Get with a page size of 5, order by last update time in descending order.
210-
curl -i "http://localhost:4000/api/v1/model_registry/model-registry/registered_models?pageSize=5&orderBy=LAST_UPDATE_TIME&sortOrder=DESC"
212+
curl -i -H "kubeflow-userid: [email protected]" "http://localhost:4000/api/v1/model_registry/model-registry/registered_models?pageSize=5&orderBy=LAST_UPDATE_TIME&sortOrder=DESC"
211213
```

clients/ui/bff/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ require (
1010
github.com/onsi/gomega v1.35.1
1111
github.com/stretchr/testify v1.9.0
1212
k8s.io/api v0.31.2
13-
k8s.io/apimachinery v0.31.2
13+
k8s.io/apimachinery v0.31.3
1414
k8s.io/client-go v0.31.2
1515
sigs.k8s.io/controller-runtime v0.19.1
1616
)

0 commit comments

Comments
 (0)