Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
75b1ffd
initial changes
hmtosi Mar 16, 2026
dea5ae8
update to include all Client parameters
hmtosi Mar 17, 2026
98b09be
remove UI components
hmtosi Mar 17, 2026
3181b0b
remove ui components
hmtosi Mar 17, 2026
0a7b6ce
remove test until functionality is confirmed
hmtosi Mar 17, 2026
64f7682
Merge branch 'main' into issue-680-configure-server
hmtosi Mar 17, 2026
2f35252
Merge branch 'main' into issue-680-configure-server
hmtosi Mar 18, 2026
ce8b94e
finish removing ui functionality
hmtosi Mar 18, 2026
2a5525e
add unit test for kfp server config
hmtosi Mar 18, 2026
a99999d
update paramter descriptions
hmtosi Mar 18, 2026
a406305
Merge branch 'main' into issue-680-configure-server
hmtosi Mar 20, 2026
9b77459
fix linting
hmtosi Mar 20, 2026
0ea0c75
change to factory architecture
hmtosi Mar 20, 2026
469368b
add factory to git tracking
hmtosi Mar 20, 2026
3633771
update documentation
hmtosi Mar 20, 2026
4a1370b
update so e2e passes
hmtosi Mar 20, 2026
808c32f
Merge branch 'main' into issue-680-configure-server
hmtosi Mar 20, 2026
92b7567
Merge branch 'kubeflow:main' into issue-680-configure-server
hmtosi Mar 24, 2026
136e2dd
update default config path
hmtosi Mar 24, 2026
0500397
update kfp authentication to allow credentials
hmtosi Mar 24, 2026
57e7e12
add authenticator file
hmtosi Mar 24, 2026
33cd1e3
Merge branch 'kubeflow:main' into issue-680-configure-server
hmtosi Mar 25, 2026
5d3ac89
Fix circular import
jesuino Mar 25, 2026
351638b
Merge pull request #2 from jesuino/credentials_circular_import
hmtosi Mar 25, 2026
f41d494
Apply suggestion from @Copilot
hmtosi Mar 25, 2026
192cded
add config env var so it can be explicitly set
hmtosi Mar 25, 2026
1a80fe5
add atomic rename suggested by copilot
hmtosi Mar 26, 2026
59703ae
update documentation
hmtosi Mar 26, 2026
3ba3ece
Merge branch 'kubeflow:main' into issue-680-configure-server
hmtosi Apr 1, 2026
481266c
separate configuration (persisted) from credentials (resolved at runt…
hmtosi Apr 1, 2026
bec5c32
prevent tokens from being saved to disk
hmtosi Apr 2, 2026
bd43a9b
Merge branch 'kubeflow:main' into issue-680-configure-server
hmtosi Apr 3, 2026
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
75 changes: 75 additions & 0 deletions kale/common/kfp_client_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Copyright 2026 The Kubeflow Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Factory for creating KFP client instances with configuration support."""

from typing import TYPE_CHECKING

import kfp

from kale.config import kfp_server_config

if TYPE_CHECKING:
from kfp import Client


def get_kfp_client(
host: str | None = None,
cookies: str | None = None,
credentials: str | None = None,
existing_token: str | None = None,
namespace: str | None = None,
ssl_ca_cert: str | None = None,
) -> "Client":
"""Create a KFP client with configuration.

Loads saved configuration from ~/.kale/kfp_server_config.json and allows
parameter overrides. Explicit parameters override saved config if they are provided.

Args:
host: KFP API server host
cookies: Authentication cookies
credentials: Service account credentials
existing_token: Bearer token for authentication
namespace: Kubernetes namespace
ssl_ca_cert: Path to CA certificate file

Returns:
kfp.Client instance configured with provided parameters or saved config
"""
# Load saved configuration
config = kfp_server_config.load_config()

# Use parameter if provided, otherwise fall back to config
if host is None:
host = config.host
if cookies is None:
cookies = config.cookies
if credentials is None:
credentials = config.credentials
if existing_token is None:
existing_token = config.existing_token
if namespace is None:
namespace = config.namespace or "kubeflow"
if ssl_ca_cert is None:
ssl_ca_cert = config.ssl_ca_cert

return kfp.Client(
host=host,
cookies=cookies,
credentials=credentials,
existing_token=existing_token,
namespace=namespace,
ssl_ca_cert=ssl_ca_cert,
)
18 changes: 7 additions & 11 deletions kale/common/kfputils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import kfp
from kfp_server_api.exceptions import ApiException

from kale.common import utils
from kale.common import kfp_client_factory, utils

KFP_RUN_ID_LABEL_KEY = "pipeline/runid"
KFP_RUN_NAME_ANNOTATION_KEY = "pipelines.kubeflow.org/run_name"
Expand All @@ -40,10 +40,6 @@
log = logging.getLogger(__name__)


def _get_kfp_client(host=None, namespace: str = "kubeflow"):
return kfp.Client(host=host, namespace=namespace)


def get_pipeline_id(pipeline_name: str, host: str = None) -> str:
"""List through the existing pipelines and filter by pipeline name.

Expand All @@ -54,7 +50,7 @@ def get_pipeline_id(pipeline_name: str, host: str = None) -> str:
Returns:
The matching pipeline id. None if not found
"""
client = _get_kfp_client(host)
client = kfp_client_factory.get_kfp_client(host)
token = ""
pipeline_id = None
while pipeline_id is None and token is not None:
Expand All @@ -80,7 +76,7 @@ def get_pipeline_version_id(version_name: str, pipeline_id: str, host: str = Non
Returns:
The matching pipeline id. None if not found
"""
client = _get_kfp_client(host)
client = kfp_client_factory.get_kfp_client(host)
page_token = ""
version_id = None
while version_id is None and page_token is not None:
Expand Down Expand Up @@ -129,7 +125,7 @@ def upload_pipeline(
host: custom host when executing outside of the cluster
Returns: (pipeline_id, version_id)
"""
client = _get_kfp_client(host)
client = kfp_client_factory.get_kfp_client(host)
log.info("Uploading pipeline '%s'...", pipeline_name)
pipeline_id = get_pipeline_id(pipeline_name, host=host)
if not pipeline_id:
Expand Down Expand Up @@ -176,7 +172,7 @@ def run_pipeline(
Returns:
Pipeline run metadata
"""
client = _get_kfp_client(host)
client = kfp_client_factory.get_kfp_client(host)
log.info("Creating KFP experiment '%s'...", experiment_name)
client.create_experiment(experiment_name)
pipeline = client.get_pipeline(pipeline_id)
Expand Down Expand Up @@ -279,7 +275,7 @@ def get_experiment_from_run_id(run_id: str):
Returns: ApiExperiment - the KFP Experiment which owns the run
"""
log.info("Getting experiment from run with ID '%s'...", run_id)
client = _get_kfp_client()
client = kfp_client_factory.get_kfp_client()
run = client.runs.get_run(run_id=run_id).run
experiment_id = None
type_experiment = client.api_models.ApiResourceType.EXPERIMENT
Expand All @@ -295,7 +291,7 @@ def get_experiment_from_run_id(run_id: str):

def get_run(run_id: str, host: str = None):
"""Retrieve KFP run based on RunID."""
client = _get_kfp_client(host)
client = kfp_client_factory.get_kfp_client(host)
return client.get_run(run_id)


Expand Down
84 changes: 84 additions & 0 deletions kale/config/kfp_server_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Copyright 2026 The Kubeflow Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json
import logging
import os
from typing import Any

from kale.config.config import Config, Field

log = logging.getLogger(__name__)


class KFPServerConfig(Config):
"""Configuration for KFP server connection."""

host = Field(type=str, default=None)
cookies = Field(type=str, default=None)
credentials = Field(type=str, default=None)
existing_token = Field(type=str, default=None)
namespace = Field(type=str, default="kubeflow")
ssl_ca_cert = Field(type=str, default=None)


def get_config_path() -> str:
"""Get the path to the KFP server configuration file."""
kale_dir = os.path.join(os.path.expanduser("~"), ".kale")
return os.path.join(kale_dir, "kfp_server_config.json")


def load_config() -> KFPServerConfig:
"""Load KFP server configuration from disk.

Returns:
KFPServerConfig instance. If no config file exists, returns default config.
"""
config_path = get_config_path()
if not os.path.exists(config_path):
log.debug("No KFP server config found at %s, using defaults", config_path)
return KFPServerConfig()

try:
with open(config_path) as f:
config_dict = json.load(f)
log.info("Loaded KFP server config from %s", config_path)
return KFPServerConfig(**config_dict)
except (json.JSONDecodeError, OSError) as e:
log.warning("Failed to load KFP server config from %s: %s. Using defaults.", config_path, e)
return KFPServerConfig()


def save_config(config: KFPServerConfig | dict[str, Any]) -> None:
"""Save KFP server configuration to disk.

Args:
config: KFPServerConfig instance or dict with config values
"""
if isinstance(config, dict):
config = KFPServerConfig(**config)

config_path = get_config_path()
kale_dir = os.path.dirname(config_path)

# Create .kale directory if it doesn't exist
os.makedirs(kale_dir, exist_ok=True)

config_dict = config.to_dict()
with open(config_path, "w") as f:
json.dump(config_dict, f, indent=2)

# Set restrictive permissions for security (only owner can read/write)
os.chmod(config_path, 0o600)
log.info("Saved KFP server config to %s", config_path)
6 changes: 3 additions & 3 deletions kale/rpc/kfp.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import kfp

from kale.common import kfputils
from kale.common import kfp_client_factory, kfputils


def _get_client(host=None):
return kfp.Client()
"""Get a KFP client using saved configuration."""
return kfp_client_factory.get_kfp_client(host=host)


def ping(request):
Expand Down
Loading
Loading