diff --git a/api/v1alpha1/dspipeline_types.go b/api/v1alpha1/dspipeline_types.go index 1dcb30a9c..8cbf80bf3 100644 --- a/api/v1alpha1/dspipeline_types.go +++ b/api/v1alpha1/dspipeline_types.go @@ -86,6 +86,19 @@ type APIServer struct { // for the api server to use instead. CustomServerConfig *ScriptConfigMap `json:"customServerConfigMap,omitempty"` + // When specified, the `data` contents of the `kfp-launcher` ConfigMap that DSPO writes + // will be fully replaced with the `data` contents of the ConfigMap specified here. + // This allows the user to fully replace the `data` contents of the kfp-launcher ConfigMap. + // The `kfp-launcher` component requires a ConfigMap to exist in the namespace + // where it runs (i.e. the namespace where pipelines run). This ConfigMap contains + // object storage configuration, as well as pipeline root (object store root path + // where artifacts will be uploaded) configuration. Currently this ConfigMap *must* + // be named "kfp-launcher". We currently deploy a default copy of the kfp-launcher + // ConfigMap via DSPO, but a user may want to provide their own ConfigMap configuration, + // so that they can specify multiple object storage sources and paths. + // +kubebuilder:validation:Optional + CustomKfpLauncherConfigMap string `json:"customKfpLauncherConfigMap,omitempty"` + // Default: true // Deprecated: DSP V1 only, will be removed in the future. // +kubebuilder:default:=true diff --git a/config/crd/bases/datasciencepipelinesapplications.opendatahub.io_datasciencepipelinesapplications.yaml b/config/crd/bases/datasciencepipelinesapplications.opendatahub.io_datasciencepipelinesapplications.yaml index a8af2afb7..cf0807800 100644 --- a/config/crd/bases/datasciencepipelinesapplications.opendatahub.io_datasciencepipelinesapplications.yaml +++ b/config/crd/bases/datasciencepipelinesapplications.opendatahub.io_datasciencepipelinesapplications.yaml @@ -112,6 +112,21 @@ spec: description: 'Default: true Deprecated: DSP V1 only, will be removed in the future.' type: boolean + customKfpLauncherConfigMap: + description: When specified, the `data` contents of the `kfp-launcher` + ConfigMap that DSPO writes will be fully replaced with the `data` + contents of the ConfigMap specified here. This allows the user + to fully replace the `data` contents of the kfp-launcher ConfigMap. + The `kfp-launcher` component requires a ConfigMap to exist in + the namespace where it runs (i.e. the namespace where pipelines + run). This ConfigMap contains object storage configuration, + as well as pipeline root (object store root path where artifacts + will be uploaded) configuration. Currently this ConfigMap *must* + be named "kfp-launcher". We currently deploy a default copy + of the kfp-launcher ConfigMap via DSPO, but a user may want + to provide their own ConfigMap configuration, so that they can + specify multiple object storage sources and paths. + type: string customServerConfigMap: description: CustomServerConfig is a custom config file that you can provide for the api server to use instead. diff --git a/config/internal/apiserver/default/kfp_launcher_config.yaml.tmpl b/config/internal/apiserver/default/kfp_launcher_config.yaml.tmpl index 1d497b584..8d85bf00b 100644 --- a/config/internal/apiserver/default/kfp_launcher_config.yaml.tmpl +++ b/config/internal/apiserver/default/kfp_launcher_config.yaml.tmpl @@ -1,5 +1,8 @@ apiVersion: v1 data: + {{ if .APIServer.CustomKfpLauncherConfigMap }} + {{.CustomKfpLauncherConfigMapData}} + {{ else }} {{ if .ObjectStorageConnection.BasePath }} defaultPipelineRoot: s3://{{.ObjectStorageConnection.Bucket}}/{{.ObjectStorageConnection.BasePath}} {{ else }} @@ -13,6 +16,7 @@ data: secretName: {{.ObjectStorageConnection.CredentialsSecret.SecretName}} accessKeyKey: {{.ObjectStorageConnection.CredentialsSecret.AccessKey}} secretKeyKey: {{.ObjectStorageConnection.CredentialsSecret.SecretKey}} + {{ end }} kind: ConfigMap metadata: name: kfp-launcher diff --git a/config/samples/v2/dspa-all-fields/dspa_all_fields.yaml b/config/samples/v2/dspa-all-fields/dspa_all_fields.yaml index ab66c9474..8697a4611 100644 --- a/config/samples/v2/dspa-all-fields/dspa_all_fields.yaml +++ b/config/samples/v2/dspa-all-fields/dspa_all_fields.yaml @@ -11,6 +11,7 @@ metadata: spec: dspVersion: v2 apiServer: + customKfpLauncherConfigMap: configmapname deploy: true enableSamplePipeline: true image: quay.io/opendatahub/ds-pipelines-api-server:latest diff --git a/controllers/dspipeline_params.go b/controllers/dspipeline_params.go index 3c17c9bd4..aa5364ef4 100644 --- a/controllers/dspipeline_params.go +++ b/controllers/dspipeline_params.go @@ -66,6 +66,7 @@ type DSPAParams struct { Minio *dspa.Minio MLMD *dspa.MLMD WorkflowController *dspa.WorkflowController + CustomKfpLauncherConfigMapData string DBConnection ObjectStorageConnection @@ -646,6 +647,29 @@ func (p *DSPAParams) ExtractParams(ctx context.Context, dsp *dspa.DataSciencePip } } + if p.APIServer.CustomKfpLauncherConfigMap != "" { + cm, err := util.GetConfigMap(ctx, p.APIServer.CustomKfpLauncherConfigMap, p.Namespace, client) + if err != nil { + if apierrs.IsNotFound(err) { + log.Info(fmt.Sprintf("ConfigMap referenced by CustomKfpLauncherConfig not found: [%s], Error: %v", p.APIServer.CustomKfpLauncherConfigMap, err)) + return err + } else { + log.Info(fmt.Sprintf("Error fetching ConfigMap referenced by CustomKfpLauncherConfig: [%s], Error: %v", p.APIServer.CustomKfpLauncherConfigMap, err)) + return err + } + + } else { + // when setting a map into the `data` field of a ConfigMap, text/template works well with a json object + jsonData, err := json.Marshal(cm.Data) + if err != nil { + log.Info(fmt.Sprintf("Error reading data of ConfigMap referenced by CustomKfpLauncherConfig: [%s], Error: %v", p.APIServer.CustomKfpLauncherConfigMap, err)) + return err + } else { + p.CustomKfpLauncherConfigMapData = string(jsonData) + } + } + } + // Track whether the "ca-bundle.crt" configmap key from odh-trusted-ca bundle // was found, this will be used to decide whether we need to account for this // ourselves later or not. diff --git a/controllers/dspipeline_params_test.go b/controllers/dspipeline_params_test.go index 65e5c63c3..1ff379b19 100644 --- a/controllers/dspipeline_params_test.go +++ b/controllers/dspipeline_params_test.go @@ -18,13 +18,15 @@ limitations under the License. package controllers import ( + "encoding/json" + "testing" + dspav1alpha1 "github.com/opendatahub-io/data-science-pipelines-operator/api/v1alpha1" "github.com/opendatahub-io/data-science-pipelines-operator/controllers/testutil" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" - "testing" ) type Client struct { @@ -258,3 +260,26 @@ func TestExtractParams_CABundle(t *testing.T) { func strPtr(v string) *string { return &v } + +func TestExtractParams_WithCustomKfpLauncherConfigMap(t *testing.T) { + ctx, params, client := CreateNewTestObjects() + cmDataExpected := map[string]string{ + "this-is-the-only-thing": "that-should-be-in-kfp-launcher-now", + } + cm := v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-custom-kfp-launcher", + Namespace: "testnamespace", + }, + Data: cmDataExpected, + } + err := client.Create(ctx, &cm) + assert.Nil(t, err) + + dspa := testutil.CreateDSPAWithCustomKfpLauncherConfigMap("my-custom-kfp-launcher") + err = params.ExtractParams(ctx, dspa, client.Client, client.Log) + assert.Nil(t, err) + + cmDataExpectedJson, err := json.Marshal(cmDataExpected) + assert.Equal(t, string(cmDataExpectedJson), params.CustomKfpLauncherConfigMapData) +} diff --git a/controllers/testutil/util.go b/controllers/testutil/util.go index 2bee136f7..d6a2c7f43 100644 --- a/controllers/testutil/util.go +++ b/controllers/testutil/util.go @@ -256,3 +256,17 @@ func CreateDSPAWithAPIServerPodtoPodTlsEnabled() *dspav1alpha1.DataSciencePipeli func boolPtr(b bool) *bool { return &b } + +func CreateDSPAWithCustomKfpLauncherConfigMap(configMapName string) *dspav1alpha1.DataSciencePipelinesApplication { + dspa := CreateEmptyDSPA() + dspa.Spec.DSPVersion = "v2" + // required, or we get an error because OCP certs aren't found + dspa.Spec.PodToPodTLS = boolPtr(false) + // required, or we get an error because this is required in v2 + dspa.Spec.MLMD.Deploy = true + dspa.Spec.APIServer = &dspav1alpha1.APIServer{ + Deploy: true, + CustomKfpLauncherConfigMap: configMapName, + } + return dspa +}