@@ -29,6 +29,10 @@ KFP_NAMESPACE="${1:?KFP namespace required}"
2929MLFLOW_NAMESPACE=" ${2:? MLflow namespace required} "
3030CONFIG_JSON_PATH=" ${3:? Path to source config.json required} "
3131
32+ MLFLOW_CA_CONFIGMAP=" mlflow-ca-cert"
33+ KFP_CA_BUNDLE_DIR=" /kfp/certs"
34+ KFP_CA_BUNDLE_PATH=" ${KFP_CA_BUNDLE_DIR} /ca.crt"
35+
3236echo " Services in ${MLFLOW_NAMESPACE} namespace:"
3337kubectl get svc -n " $MLFLOW_NAMESPACE " --no-headers
3438MLFLOW_SVC=$( kubectl get svc -n " $MLFLOW_NAMESPACE " --no-headers -o custom-columns=" :metadata.name" | grep -i mlflow | head -1)
@@ -42,9 +46,41 @@ MLFLOW_STATIC_PREFIX="/mlflow"
4246MLFLOW_ENDPOINT=" https://${MLFLOW_HOST} :${MLFLOW_PORT}${MLFLOW_STATIC_PREFIX} "
4347echo " MLflow service: $MLFLOW_SVC port=$MLFLOW_PORT endpoint=$MLFLOW_ENDPOINT "
4448
45- MLFLOW_PATCH=$( jq -n --arg endpoint " $MLFLOW_ENDPOINT " ' {
49+ # --- Extract CA certificate from the MLflow TLS secret ---
50+ CA_CERT_FILE=" /tmp/mlflow-ca.crt"
51+
52+ echo " Extracting MLflow CA certificate..."
53+ TLS_SECRET=$( kubectl get secret -n " $MLFLOW_NAMESPACE " -o custom-columns=" :metadata.name" --no-headers | grep -i " mlflow.*tls\|tls.*mlflow" | head -1)
54+ if [ -z " $TLS_SECRET " ]; then
55+ TLS_SECRET=$( kubectl get secret -n " $MLFLOW_NAMESPACE " --field-selector type=kubernetes.io/tls -o custom-columns=" :metadata.name" --no-headers | head -1)
56+ fi
57+ if [ -z " $TLS_SECRET " ]; then
58+ echo " ERROR: No TLS secret found in namespace $MLFLOW_NAMESPACE "
59+ exit 1
60+ fi
61+ echo " Found TLS secret: $TLS_SECRET "
62+
63+ # cert-manager stores the CA in the ca.crt key; fall back to tls.crt if ca.crt is absent
64+ CA_DATA=$( kubectl get secret -n " $MLFLOW_NAMESPACE " " $TLS_SECRET " -o jsonpath=' {.data.ca\.crt}' 2> /dev/null)
65+ if [ -z " $CA_DATA " ]; then
66+ echo " ca.crt not found in secret, falling back to tls.crt"
67+ CA_DATA=$( kubectl get secret -n " $MLFLOW_NAMESPACE " " $TLS_SECRET " -o jsonpath=' {.data.tls\.crt}' )
68+ fi
69+ if [ -z " $CA_DATA " ]; then
70+ echo " ERROR: Could not extract CA certificate from secret $TLS_SECRET "
71+ exit 1
72+ fi
73+ echo " $CA_DATA " | base64 -d > " $CA_CERT_FILE "
74+ echo " CA certificate extracted to $CA_CERT_FILE ($( wc -l < " $CA_CERT_FILE " ) lines)"
75+
76+ # ConfigMap consumed by the API server (plugin config) and compiler (driver/launcher pods).
77+ kubectl create configmap " $MLFLOW_CA_CONFIGMAP " -n " $KFP_NAMESPACE " \
78+ --from-file=ca.crt=" $CA_CERT_FILE " --dry-run=client -o yaml | kubectl apply -f -
79+
80+ # --- Build the MLflow plugin config with caBundlePath ---
81+ MLFLOW_PATCH=$( jq -n --arg endpoint " $MLFLOW_ENDPOINT " --arg caBundlePath " $KFP_CA_BUNDLE_PATH " ' {
4682 endpoint: $endpoint,
47- tls: { insecureSkipVerify: true },
83+ tls: { caBundlePath: $caBundlePath },
4884 settings: { workspacesEnabled: true }
4985}' )
5086
@@ -54,10 +90,11 @@ jq --argjson mlflow "$MLFLOW_PATCH" '. + { plugins: { mlflow: $mlflow } }' \
5490echo " Patched config.json plugins.mlflow:"
5591jq ' .plugins.mlflow' /tmp/kfp-config.json
5692
93+ # --- Deploy plugin config and wire CA trust on the API server + compiler ---
5794kubectl create configmap kfp-mlflow-config -n " $KFP_NAMESPACE " \
5895 --from-file=config.json=/tmp/kfp-config.json --dry-run=client -o yaml | kubectl apply -f -
5996kubectl patch deployment ml-pipeline -n " $KFP_NAMESPACE " --type=strategic -p \
60- ' {"spec":{"template":{"spec":{"volumes":[{"name":"mlflow-cfg","configMap":{"name":"kfp-mlflow-config"}}],"containers":[{"name":"ml-pipeline-api-server","volumeMounts":[{"name":"mlflow-cfg","mountPath":"/config/config.json","subPath":"config.json"}]}]}}}}'
97+ ' {"spec":{"template":{"spec":{"volumes":[{"name":"mlflow-cfg","configMap":{"name":"kfp-mlflow-config"}},{"name":"mlflow-ca","configMap":{"name":" ' " $MLFLOW_CA_CONFIGMAP " ' "}} ],"containers":[{"name":"ml-pipeline-api-server","env":[{"name":"CABUNDLE_CONFIGMAP_NAME","value":" ' " $MLFLOW_CA_CONFIGMAP " ' "}]," volumeMounts":[{"name":"mlflow-cfg","mountPath":"/config/config.json","subPath":"config.json"},{"name":"mlflow-ca","mountPath":" ' " $KFP_CA_BUNDLE_DIR " ' ","readOnly":true }]}]}}}}'
6198kubectl rollout status deployment/ml-pipeline -n " $KFP_NAMESPACE " --timeout=180s
6299
63100pkill -f " kubectl port-forward.*ml-pipeline.*8888" || true
@@ -87,12 +124,13 @@ if [ -n "${GITHUB_ENV:-}" ]; then
87124 echo " MLFLOW_PORT_FORWARD_NS=$MLFLOW_NAMESPACE " >> " $GITHUB_ENV "
88125 echo " MLFLOW_PORT_FORWARD_SVC=$MLFLOW_SVC " >> " $GITHUB_ENV "
89126 echo " MLFLOW_PORT_FORWARD_REMOTE_PORT=$MLFLOW_PORT " >> " $GITHUB_ENV "
127+ echo " MLFLOW_CA_BUNDLE_PATH=$CA_CERT_FILE " >> " $GITHUB_ENV "
90128 if [ -n " $SA_TOKEN " ]; then
91129 echo " MLFLOW_BEARER_TOKEN=$SA_TOKEN " >> " $GITHUB_ENV "
92- echo " Exported MLFLOW_BEARER_TOKEN and MLFLOW_WORKSPACE for test helpers"
130+ echo " Exported MLFLOW_BEARER_TOKEN, MLFLOW_WORKSPACE, and MLFLOW_CA_BUNDLE_PATH for test helpers"
93131 else
94132 echo " WARNING: Could not create SA token; MLflow requests may be unauthenticated"
95- echo " Exported MLFLOW_WORKSPACE only"
133+ echo " Exported MLFLOW_WORKSPACE and MLFLOW_CA_BUNDLE_PATH only"
96134 fi
97135fi
98136
@@ -105,7 +143,7 @@ CURL_HEADERS=(-H "X-MLflow-Workspace: $KFP_NAMESPACE")
105143
106144STATUS=000
107145for i in $( seq 1 30) ; do
108- STATUS=$( curl -sk -o /dev/null -w ' %{http_code}' --connect-timeout 5 --max-time 10 \
146+ STATUS=$( curl -s --cacert " $CA_CERT_FILE " -o /dev/null -w ' %{http_code}' --connect-timeout 5 --max-time 10 \
109147 " ${CURL_HEADERS[@]} " " $HEALTH_URL " 2> /dev/null || echo " 000" )
110148 if [ " $STATUS " != " 000" ] && [ " $STATUS " -lt 500 ] 2> /dev/null; then
111149 echo " MLflow backend is healthy on localhost:8080 (HTTPS, status=$STATUS )"
0 commit comments