@@ -17,6 +17,7 @@ package driver
17
17
import (
18
18
"context"
19
19
"encoding/json"
20
+ "errors"
20
21
"fmt"
21
22
"slices"
22
23
"strconv"
@@ -51,6 +52,8 @@ var dummyImages = map[string]string{
51
52
"argostub/deletepvc" : "delete PVC" ,
52
53
}
53
54
55
+ var ErrResolvedParameterNull = errors .New ("the resolved input parameter is null" )
56
+
54
57
// TODO(capri-xiyue): Move driver to component package
55
58
// Driver options
56
59
type Options struct {
@@ -191,6 +194,7 @@ func RootDAG(ctx context.Context, opts Options, mlmd *metadata.Client) (executio
191
194
if err != nil {
192
195
return nil , err
193
196
}
197
+
194
198
executorInput := & pipelinespec.ExecutorInput {
195
199
Inputs : & pipelinespec.ExecutorInput_Inputs {
196
200
ParameterValues : opts .RuntimeConfig .GetParameterValues (),
@@ -803,7 +807,7 @@ func extendPodSpecPatch(
803
807
for _ , secretAsVolume := range kubernetesExecutorConfig .GetSecretAsVolume () {
804
808
var secretName string
805
809
if secretAsVolume .SecretNameParameter != nil {
806
- resolvedSecretName , err := resolveInputParameter (ctx , dag , pipeline , opts , mlmd ,
810
+ resolvedSecretName , err := resolveInputParameterStr (ctx , dag , pipeline , opts , mlmd ,
807
811
secretAsVolume .SecretNameParameter , inputParams )
808
812
if err != nil {
809
813
return fmt .Errorf ("failed to resolve secret name: %w" , err )
@@ -847,7 +851,7 @@ func extendPodSpecPatch(
847
851
848
852
var secretName string
849
853
if secretAsEnv .SecretNameParameter != nil {
850
- resolvedSecretName , err := resolveInputParameter (ctx , dag , pipeline , opts , mlmd ,
854
+ resolvedSecretName , err := resolveInputParameterStr (ctx , dag , pipeline , opts , mlmd ,
851
855
secretAsEnv .SecretNameParameter , inputParams )
852
856
if err != nil {
853
857
return fmt .Errorf ("failed to resolve secret name: %w" , err )
@@ -869,7 +873,7 @@ func extendPodSpecPatch(
869
873
for _ , configMapAsVolume := range kubernetesExecutorConfig .GetConfigMapAsVolume () {
870
874
var configMapName string
871
875
if configMapAsVolume .ConfigMapNameParameter != nil {
872
- resolvedSecretName , err := resolveInputParameter (ctx , dag , pipeline , opts , mlmd ,
876
+ resolvedSecretName , err := resolveInputParameterStr (ctx , dag , pipeline , opts , mlmd ,
873
877
configMapAsVolume .ConfigMapNameParameter , inputParams )
874
878
if err != nil {
875
879
return fmt .Errorf ("failed to resolve configmap name: %w" , err )
@@ -915,7 +919,7 @@ func extendPodSpecPatch(
915
919
916
920
var configMapName string
917
921
if configMapAsEnv .ConfigMapNameParameter != nil {
918
- resolvedSecretName , err := resolveInputParameter (ctx , dag , pipeline , opts , mlmd ,
922
+ resolvedSecretName , err := resolveInputParameterStr (ctx , dag , pipeline , opts , mlmd ,
919
923
configMapAsEnv .ConfigMapNameParameter , inputParams )
920
924
if err != nil {
921
925
return fmt .Errorf ("failed to resolve configmap name: %w" , err )
@@ -937,7 +941,7 @@ func extendPodSpecPatch(
937
941
for _ , imagePullSecret := range kubernetesExecutorConfig .GetImagePullSecret () {
938
942
var secretName string
939
943
if imagePullSecret .SecretNameParameter != nil {
940
- resolvedSecretName , err := resolveInputParameter (ctx , dag , pipeline , opts , mlmd ,
944
+ resolvedSecretName , err := resolveInputParameterStr (ctx , dag , pipeline , opts , mlmd ,
941
945
imagePullSecret .SecretNameParameter , inputParams )
942
946
if err != nil {
943
947
return fmt .Errorf ("failed to resolve image pull secret name: %w" , err )
@@ -1497,8 +1501,20 @@ func resolveInputs(
1497
1501
for name , paramSpec := range task .GetInputs ().GetParameters () {
1498
1502
v , err := resolveInputParameter (ctx , dag , pipeline , opts , mlmd , paramSpec , inputParams )
1499
1503
if err != nil {
1504
+ if ! errors .Is (err , ErrResolvedParameterNull ) {
1505
+ return nil , err
1506
+ }
1507
+
1508
+ componentParam , ok := opts .Component .GetInputDefinitions ().GetParameters ()[name ]
1509
+ if ok && componentParam != nil && componentParam .IsOptional {
1510
+ // If the resolved paramter was null and the component input parameter is optional, just skip setting
1511
+ // it and the launcher will handle defaults.
1512
+ continue
1513
+ }
1514
+
1500
1515
return nil , err
1501
1516
}
1517
+
1502
1518
inputs .ParameterValues [name ] = v
1503
1519
}
1504
1520
@@ -1515,7 +1531,9 @@ func resolveInputs(
1515
1531
}
1516
1532
1517
1533
// resolveInputParameter resolves an InputParameterSpec
1518
- // using a given input context via InputParams.
1534
+ // using a given input context via InputParams. ErrResolvedParameterNull is returned if paramSpec
1535
+ // is a component input parameter and parameter resolves to a null value (i.e. an optional pipeline input with no
1536
+ // default). The caller can decide if this is allowed in that context.
1519
1537
func resolveInputParameter (
1520
1538
ctx context.Context ,
1521
1539
dag * metadata.DAG ,
@@ -1539,6 +1557,13 @@ func resolveInputParameter(
1539
1557
if ! ok {
1540
1558
return nil , paramError (fmt .Errorf ("parent DAG does not have input parameter %s" , componentInput ))
1541
1559
}
1560
+
1561
+ if _ , isNullValue := v .GetKind ().(* structpb.Value_NullValue ); isNullValue {
1562
+ // Null values are only allowed for optional pipeline input parameters with no values. The caller has this
1563
+ // context to know if this is allowed.
1564
+ return nil , fmt .Errorf ("%w: %s" , ErrResolvedParameterNull , componentInput )
1565
+ }
1566
+
1542
1567
return v , nil
1543
1568
1544
1569
// This is the case where the input comes from the output of an upstream task.
@@ -1588,6 +1613,33 @@ func resolveInputParameter(
1588
1613
}
1589
1614
}
1590
1615
1616
+ // resolveInputParameterStr is like resolveInputParameter but returns an error if the resolved value is not a non-empty
1617
+ // string.
1618
+ func resolveInputParameterStr (
1619
+ ctx context.Context ,
1620
+ dag * metadata.DAG ,
1621
+ pipeline * metadata.Pipeline ,
1622
+ opts Options ,
1623
+ mlmd * metadata.Client ,
1624
+ paramSpec * pipelinespec.TaskInputsSpec_InputParameterSpec ,
1625
+ inputParams map [string ]* structpb.Value ,
1626
+ ) (* structpb.Value , error ) {
1627
+ val , err := resolveInputParameter (ctx , dag , pipeline , opts , mlmd , paramSpec , inputParams )
1628
+ if err != nil {
1629
+ return nil , err
1630
+ }
1631
+
1632
+ if typedVal , ok := val .GetKind ().(* structpb.Value_StringValue ); ok && typedVal != nil {
1633
+ if typedVal .StringValue == "" {
1634
+ return nil , fmt .Errorf ("resolving input parameter with spec %s. Expected a non-empty string." , paramSpec )
1635
+ }
1636
+ } else {
1637
+ return nil , fmt .Errorf ("resolving input parameter with spec %s. Expected a string but got: %T" , paramSpec , val .GetKind ())
1638
+ }
1639
+
1640
+ return val , nil
1641
+ }
1642
+
1591
1643
// resolveInputArtifact resolves an InputArtifactSpec
1592
1644
// using a given input context via inputArtifacts.
1593
1645
func resolveInputArtifact (
@@ -2366,7 +2418,7 @@ func makeVolumeMountPatch(
2366
2418
}
2367
2419
}
2368
2420
2369
- resolvedPvcName , err := resolveInputParameter (ctx , dag , pipeline , opts , mlmd ,
2421
+ resolvedPvcName , err := resolveInputParameterStr (ctx , dag , pipeline , opts , mlmd ,
2370
2422
pvcNameParameter , inputParams )
2371
2423
if err != nil {
2372
2424
return nil , nil , fmt .Errorf ("failed to resolve pvc name: %w" , err )
0 commit comments