Repository: kubeflow/pipelines

Issue:
[SDK] Support `dnsConfig` field
Is there any way to set up `dnsConfig` using the SDK?

To avoid [DNS failure due to the default `ndots: 5`](https://github.com/kubernetes/dns/issues/159#issuecomment-336449931), I have added the following directly to the compiled YAML file.
(Argo has supported dnsConfig since [2.3.0](https://github.com/argoproj/argo/releases/tag/v2.3.0).)
```yaml
  dnsConfig:
    options:
    - {name: ndots, value: '1'}
```


Discussion:

Resolving patch:
diff --git a/samples/core/dns_config/dns_config.py b/samples/core/dns_config/dns_config.py
new file mode 100644
--- /dev/null
+++ b/samples/core/dns_config/dns_config.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import kfp
+from kfp import dsl
+from kubernetes.client.models import V1PodDNSConfig, V1PodDNSConfigOption
+
+
+def echo_op():
+    return dsl.ContainerOp(
+        name='echo',
+        image='library/bash:4.4.23',
+        command=['sh', '-c'],
+        arguments=['echo "hello world"']
+    )
+
+
+@dsl.pipeline(
+    name='dnsConfig setting',
+    description='Passes dnsConfig setting to workflow.'
+)
+def dns_config_pipeline():
+    echo_task = echo_op()
+
+
+if __name__ == '__main__':
+    pipeline_conf = kfp.dsl.PipelineConf()
+    pipeline_conf.set_dns_config(dns_config=V1PodDNSConfig(
+        nameservers=["1.2.3.4"],
+        options=[V1PodDNSConfigOption(name="ndots", value="2")]
+    ))
+
+    kfp.compiler.Compiler().compile(
+        dns_config_pipeline,
+        __file__ + '.yaml',
+        pipeline_conf=pipeline_conf
+    )
diff --git a/sdk/python/kfp/compiler/compiler.py b/sdk/python/kfp/compiler/compiler.py
--- a/sdk/python/kfp/compiler/compiler.py
+++ b/sdk/python/kfp/compiler/compiler.py
@@ -702,6 +702,9 @@ def _create_pipeline_workflow(self, parameter_defaults, pipeline, op_transformer
     if pipeline_conf.default_pod_node_selector:
       workflow['spec']['nodeSelector'] = pipeline_conf.default_pod_node_selector
 
+    if pipeline_conf.dns_config:
+      workflow['spec']['dnsConfig'] = convert_k8s_obj_to_json(pipeline_conf.dns_config)
+
     if pipeline_conf.image_pull_policy != None:
       if pipeline_conf.image_pull_policy in ["Always", "Never", "IfNotPresent"]:
         for template in workflow["spec"]["templates"]:
diff --git a/sdk/python/kfp/dsl/_pipeline.py b/sdk/python/kfp/dsl/_pipeline.py
--- a/sdk/python/kfp/dsl/_pipeline.py
+++ b/sdk/python/kfp/dsl/_pipeline.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 from typing import Union
+from kubernetes.client.models import V1PodDNSConfig
 from . import _container_op
 from . import _resource_op
 from . import _ops_group
@@ -66,6 +67,7 @@ def __init__(self):
     self.image_pull_policy = None
     self.parallelism = None
     self._data_passing_method = None
+    self.dns_config = None
 
   def set_image_pull_secrets(self, image_pull_secrets):
     """Configures the pipeline level imagepullsecret
@@ -149,6 +151,27 @@ def add_op_transformer(self, transformer):
     """
     self.op_transformers.append(transformer)
 
+  def set_dns_config(self, dns_config: V1PodDNSConfig):
+    """Set the dnsConfig to be given to each pod.
+
+    Args:
+      dns_config: Kubernetes V1PodDNSConfig
+        For detailed description, check Kubernetes V1PodDNSConfig definition
+        https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodDNSConfig.md
+
+    Example:
+
+      ::
+        import kfp
+        from kubernetes.client.models import V1PodDNSConfig, V1PodDNSConfigOption
+        pipeline_conf = kfp.dsl.PipelineConf()
+        pipeline_conf.set_dns_config(dns_config=V1PodDNSConfig(
+          nameservers=["1.2.3.4"],
+          options=[V1PodDNSConfigOption(name="ndots", value="2")]
+        ))
+    """
+    self.dns_config = dns_config
+
   @property
   def data_passing_method(self):
     return self._data_passing_method


Test patch:
diff --git a/sdk/python/tests/compiler/compiler_tests.py b/sdk/python/tests/compiler/compiler_tests.py
--- a/sdk/python/tests/compiler/compiler_tests.py
+++ b/sdk/python/tests/compiler/compiler_tests.py
@@ -30,7 +30,7 @@
 from kfp.dsl import ContainerOp, pipeline
 from kfp.dsl.types import Integer, InconsistentTypeException
 from kubernetes.client import V1Toleration, V1Affinity, V1NodeSelector, V1NodeSelectorRequirement, V1NodeSelectorTerm, \
-  V1NodeAffinity
+  V1NodeAffinity, V1PodDNSConfig, V1PodDNSConfigOption
 
 
 def some_op():
@@ -800,6 +800,22 @@ def some_pipeline():
     workflow_dict = kfp.compiler.Compiler()._compile(some_pipeline)
     self.assertEqual(workflow_dict['spec']['nodeSelector'], {"cloud.google.com/gke-accelerator":"nvidia-tesla-p4"})
 
+  def test_set_dns_config(self):
+    """Test a pipeline with node selector."""
+    @dsl.pipeline()
+    def some_pipeline():
+      some_op()
+      dsl.get_pipeline_conf().set_dns_config(V1PodDNSConfig(
+        nameservers=["1.2.3.4"],
+        options=[V1PodDNSConfigOption(name="ndots", value="2")]
+      ))
+
+    workflow_dict = kfp.compiler.Compiler()._compile(some_pipeline)
+    self.assertEqual(
+      workflow_dict['spec']['dnsConfig'],
+      {"nameservers": ["1.2.3.4"], "options": [{"name": "ndots", "value": "2"}]}
+    )
+
   def test_container_op_output_error_when_no_or_multiple_outputs(self):
 
     def no_outputs_pipeline():