@@ -198,4 +198,81 @@ A concrete example of using the Kubeflow orchestrator can be found
198198[ here] ( https://github.com/zenml-io/zenml/tree/main/examples/kubeflow_pipelines_orchestration ) .
199199
200200For more information and a full list of configurable attributes of the Kubeflow orchestrator, check out the
201- [ API Docs] ( https://apidocs.zenml.io/latest/api_docs/integrations/#zenml.integrations.kubeflow.orchestrators.kubeflow_orchestrator.KubeflowOrchestrator ) .
201+ [ API Docs] ( https://apidocs.zenml.io/latest/api_docs/integrations/#zenml.integrations.kubeflow.orchestrators.kubeflow_orchestrator.KubeflowOrchestrator ) .
202+
203+ ## Important Note for Multi-Tenancy Deployments
204+
205+ Kubeflow has a notion of [ multi-tenancy] ( https://www.kubeflow.org/docs/components/multi-tenancy/overview/ )
206+ built into its deployment. Kubeflow’s multi-user isolation simplifies user
207+ operations because each user only views and edited\s the Kubeflow components
208+ and model artifacts defined in their configuration.
209+
210+ Currently, the default ZenML Kubeflow orchestrator yields the following error
211+ when running a pipeline:
212+
213+ ``` shell
214+ HTTP response body: {" error" :" Invalid input error: Invalid resource references for experiment. ListExperiment requires filtering by namespace." ," code" :3," message" :" Invalid input error: Invalid resource references for experiment. ListExperiment requires filtering by
215+ namespace." ," details" :[{" @type" :" type.googleapis.com/api.Error" ," error_message" :" Invalid resource references for experiment. ListExperiment requires filtering by namespace." ," error_details" :" Invalid input error: Invalid resource references for experiment. ListExperiment requires filtering by namespace." }]}
216+ ```
217+
218+ The current workaround is as follows:
219+
220+ ``` python
221+ import json
222+ import os
223+ import kfp
224+
225+ NAMESPACE = " namespace_name" # set this
226+ USERNAME = " foo" # set this
227+ PASSWORD = " bar" # set this
228+ HOST = " https://qux.com" # set this
229+ KFP_CONFIG = ' ~/.config/kfp/context.json' # set this manually if you'd like
230+
231+ def get_kfp_token (username : str , password : str ) -> str :
232+ """ Get token for kubeflow authentication."""
233+ session = requests.Session()
234+ response = session.get(HOST )
235+ headers = {
236+ " Content-Type" : " application/x-www-form-urlencoded" ,
237+ }
238+ data = {" login" : username, " password" : password}
239+ session.post(response.url, headers = headers, data = data)
240+ session_cookie = session.cookies.get_dict()[" authservice_session" ]
241+ return session_cookie
242+
243+ token = get_kfp_token()
244+ cookies = ' authservice_session=' + token
245+
246+ # 1: Set user namespace globally
247+ kfp.Client(host = HOST , cookies = cookies).set_user_namespace(NAMESPACE )
248+
249+ # 2: Set cookie globally in the kfp config file
250+ with open (KFP_CONFIG , ' r' ) as f:
251+ data = json.load(f)
252+ data[' client_authentication_cookie' ] = cookies
253+
254+ os.remove(KFP_CONFIG )
255+ with open (KFP_CONFIG , ' w' ) as f:
256+ json.dump(data, f)
257+
258+ # Continue with your normal pipeline code..
259+ ```
260+
261+ Please note that in the above code, ` HOST ` should be registered on orchestration registration,
262+ with the ` kubeflow_hostname ` parameter:
263+
264+ ```
265+ export HOST=https://qux.com
266+ zenml orchestrator register multi_tenant_kf --flavor=kubeflow \
267+ --kubeflow_hostname=$(HOST)/pipeline # /pipeline is important!
268+ --other_params..
269+ ```
270+ In future ZenML versions, multi-tenancy will be natively supported. See this
271+ [ Slack thread] ( https://zenml.slack.com/archives/C01FWQ5D0TT/p1662545810395779 ) for more details
272+ on how the above workaround came to effect.
273+
274+ Please note that the above is all to initialize the ` kfp.Client() ` class in the standard orchestrator logic.
275+ This code can be seen [ here] ( https://github.com/zenml-io/zenml/blob/main/src/zenml/integrations/kubeflow/orchestrators/kubeflow_orchestrator.py#L709 ) .
276+
277+ You can simply override this logic and add your custom authentication scheme if needed. Read [ here] ( custom.md )
278+ for more details on how to create a custom orchestrator.
0 commit comments