Skip to content

Commit 3ea6b60

Browse files
authored
fix(backend/sdk): Fix REST API outputs for pipeline versions with invalid platform specs (kubeflow#12183)
* Skip setting spec.platformSpec when platform spec is empty An empty platform spec causes a bug where the REST API will not return a pipeline spec because it errantly thinks the empty platform spec is the pipeline spec. Signed-off-by: mprahl <[email protected]> * Fix REST API outputs for pipeline versions with invalid platform specs When a platform spec did not contain a `kubernetes` platform when defined as a Kubernetes manifest (e.g. `spec.platformSpec: {}`), `YamlStringToPipelineSpecStruct` was errantly detecting it as the pipeline spec when outputting the pipeline version through the REST API. This caused the REST API to not return a pipeline spec in the case of it being empty, and caused the UI to load the pipeline and to properly create a run. The solution is when translating from the Kubernetes object to a model representation, the invalid platform specs are not included. This allows for proper REST API output while also not modifying the user's spec in the mutating webhook which would cause GitOps tools to always reconcile. Signed-off-by: mprahl <[email protected]> --------- Signed-off-by: mprahl <[email protected]>
1 parent de89b9c commit 3ea6b60

File tree

4 files changed

+121
-3
lines changed

4 files changed

+121
-3
lines changed

backend/src/crd/kubernetes/v2beta1/pipelineversion_types.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,15 @@ func (p *PipelineVersion) ToModel() (*model.PipelineVersion, error) {
179179
piplineSpecAndPlatformSpec = append(piplineSpecAndPlatformSpec, platformSpecBytes...)
180180
}
181181

182+
// The pipeline spec in model.PipelineVersion ignores platform specs that don't have a "kubernetes" platform.
183+
// This additional parsing filters out platform specs that normally are excluded when the pipeline version is
184+
// created through the REST API. This is done rather than modifying the mutating webhook to remove these
185+
// platform specs so that GitOps tools don't see a diff from what is in Git and what is on the cluster.
186+
v2Spec, err := template.NewV2SpecTemplate(piplineSpecAndPlatformSpec, false, nil)
187+
if err != nil {
188+
return nil, fmt.Errorf("failed to parse the pipeline spec: %w", err)
189+
}
190+
182191
pipelineVersionStatus := model.PipelineVersionCreating
183192

184193
for _, condition := range p.Status.Conditions {
@@ -214,7 +223,7 @@ func (p *PipelineVersion) ToModel() (*model.PipelineVersion, error) {
214223
Status: pipelineVersionStatus,
215224
CodeSourceUrl: p.Spec.CodeSourceURL,
216225
Description: p.Spec.Description,
217-
PipelineSpec: string(piplineSpecAndPlatformSpec),
226+
PipelineSpec: string(v2Spec.Bytes()),
218227
PipelineSpecURI: p.Spec.PipelineSpecURI,
219228
}, nil
220229
}

backend/src/crd/kubernetes/v2beta1/pipelineversion_types_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,67 @@ func TestToModel_PipelineAndPlatformSpecs(t *testing.T) {
404404
assert.Contains(t, result.PipelineSpec, "---")
405405
}
406406

407+
func TestToModel_PipelineAndPlatformSpecs_WithNonKubernetesPlatform(t *testing.T) {
408+
pipelineVersion := &PipelineVersion{
409+
ObjectMeta: metav1.ObjectMeta{
410+
Name: "test-version",
411+
Namespace: "default",
412+
UID: "version-456",
413+
OwnerReferences: []metav1.OwnerReference{
414+
{
415+
APIVersion: GroupVersion.String(),
416+
Kind: "Pipeline",
417+
UID: "pipeline-123",
418+
Name: "test-pipeline",
419+
},
420+
},
421+
},
422+
Spec: PipelineVersionSpec{
423+
DisplayName: "Test Version",
424+
Description: "A test version",
425+
PipelineName: "test-pipeline",
426+
CodeSourceURL: "https://github.com/test/pipeline",
427+
PipelineSpecURI: "gs://bucket/pipeline.yaml",
428+
PipelineSpec: IRSpec{
429+
Value: map[string]interface{}{
430+
"pipelineInfo": map[string]interface{}{
431+
"name": "test-pipeline",
432+
"displayName": "Test Pipeline",
433+
},
434+
"root": map[string]interface{}{
435+
"dag": map[string]interface{}{
436+
"tasks": map[string]interface{}{},
437+
},
438+
},
439+
"schemaVersion": "2.1.0",
440+
"sdkVersion": "kfp-2.13.0",
441+
},
442+
},
443+
PlatformSpec: &IRSpec{
444+
Value: map[string]interface{}{
445+
"platforms": map[string]interface{}{
446+
"not-kubernetes": map[string]interface{}{
447+
"pipelineConfig": map[string]interface{}{
448+
"workspace": map[string]interface{}{
449+
"size": "10Gi",
450+
},
451+
},
452+
},
453+
},
454+
},
455+
},
456+
},
457+
}
458+
459+
result, err := pipelineVersion.ToModel()
460+
require.NoError(t, err)
461+
assert.NotNil(t, result)
462+
463+
// Check that the platform spec is not included
464+
assert.NotContains(t, result.PipelineSpec, "platforms:")
465+
assert.NotContains(t, result.PipelineSpec, "---")
466+
}
467+
407468
func TestToModel_WithStatus(t *testing.T) {
408469
pipelineVersion := &PipelineVersion{
409470
ObjectMeta: metav1.ObjectMeta{
@@ -426,6 +487,11 @@ func TestToModel_WithStatus(t *testing.T) {
426487
"pipelineInfo": map[string]interface{}{
427488
"name": "test-pipeline",
428489
},
490+
"root": map[string]interface{}{
491+
"dag": map[string]interface{}{
492+
"tasks": map[string]interface{}{},
493+
},
494+
},
429495
"schemaVersion": "2.1.0",
430496
},
431497
},
@@ -471,6 +537,11 @@ func TestToModel_DisplayNameFallback(t *testing.T) {
471537
"pipelineInfo": map[string]interface{}{
472538
"name": "test-pipeline",
473539
},
540+
"root": map[string]interface{}{
541+
"dag": map[string]interface{}{
542+
"tasks": map[string]interface{}{},
543+
},
544+
},
474545
"schemaVersion": "2.1.0",
475546
},
476547
},
@@ -498,6 +569,11 @@ func TestToModel_InvalidPipelineSpec(t *testing.T) {
498569
"pipelineInfo": map[string]interface{}{
499570
"name": "test-pipeline",
500571
},
572+
"root": map[string]interface{}{
573+
"dag": map[string]interface{}{
574+
"tasks": map[string]interface{}{},
575+
},
576+
},
501577
"schemaVersion": "2.1.0",
502578
},
503579
},
@@ -526,6 +602,11 @@ func TestToModel_InvalidPlatformSpec(t *testing.T) {
526602
"pipelineInfo": map[string]interface{}{
527603
"name": "test-pipeline",
528604
},
605+
"root": map[string]interface{}{
606+
"dag": map[string]interface{}{
607+
"tasks": map[string]interface{}{},
608+
},
609+
},
529610
"schemaVersion": "2.1.0",
530611
},
531612
},

sdk/python/kfp/compiler/compiler_test.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1100,8 +1100,18 @@ def my_pipeline(input1: str):
11001100
pipeline_name)
11011101
self.assertEqual(pipeline_version_manifest['metadata']['namespace'],
11021102
namespace)
1103+
self.assertNotIn('platformSpec', pipeline_version_manifest['spec'])
1104+
1105+
# Test with include_pipeline_manifest=False and has a platform spec
1106+
@dsl.pipeline(
1107+
name='my-pipeline',
1108+
description='A simple test pipeline with platform spec',
1109+
pipeline_config=dsl.PipelineConfig(
1110+
workspace=dsl.WorkspaceConfig(size='25Gi'),),
1111+
)
1112+
def my_pipeline(input1: str):
1113+
print_op(message=input1)
11031114

1104-
# Test with include_pipeline_manifest=False
11051115
package_path2 = os.path.join(tmpdir, 'pipeline2.yaml')
11061116
kubernetes_manifest_options2 = KubernetesManifestOptions(
11071117
pipeline_name=pipeline_name,
@@ -1136,6 +1146,21 @@ def my_pipeline(input1: str):
11361146
pipeline_name)
11371147
self.assertEqual(
11381148
pipeline_version_manifest2['metadata']['namespace'], namespace)
1149+
self.assertEqual(
1150+
pipeline_version_manifest2['spec']['platformSpec'], {
1151+
'platforms': {
1152+
'kubernetes': {
1153+
'pipelineConfig': {
1154+
'workspace': {
1155+
'kubernetes': {
1156+
'pvcSpecPatch': {}
1157+
},
1158+
'size': '25Gi'
1159+
}
1160+
}
1161+
}
1162+
}
1163+
})
11391164

11401165

11411166
class TestCompilePipelineCaching(unittest.TestCase):

sdk/python/kfp/compiler/pipeline_spec_builder.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2121,9 +2121,12 @@ def _write_kubernetes_manifest_to_file(
21212121
'displayName': pipeline_version_display_name,
21222122
'pipelineName': pipeline_name,
21232123
'pipelineSpec': pipeline_spec_dict,
2124-
'platformSpec': platform_spec_dict,
21252124
},
21262125
}
2126+
2127+
if platform_spec_dict:
2128+
pipeline_version_manifest['spec']['platformSpec'] = platform_spec_dict
2129+
21272130
if pipeline_description:
21282131
pipeline_version_manifest['spec']['description'] = pipeline_description
21292132
documents.append(pipeline_version_manifest)

0 commit comments

Comments
 (0)