Esempio n. 1
0
    def create_pod_request_obj(self) -> k8s.V1Pod:
        """
        Creates a V1Pod based on user parameters. Note that a `pod` or `pod_template_file`
        will supersede all other values.

        """
        self.log.debug("Creating pod for K8sPodOperator task %s", self.task_id)
        if self.pod_template_file:
            self.log.debug("Pod template file found, will parse for base pod")
            pod_template = pod_generator.PodGenerator.deserialize_model_file(
                self.pod_template_file)
        else:
            pod_template = k8s.V1Pod(metadata=k8s.V1ObjectMeta(name="name"))

        pod = k8s.V1Pod(api_version="v1",
                        kind="Pod",
                        metadata=k8s.V1ObjectMeta(
                            namespace=self.namespace,
                            labels=self.labels,
                            name=self.name,
                            annotations=self.annotations,
                        ),
                        spec=k8s.V1PodSpec(
                            node_selector=self.node_selectors,
                            affinity=self.affinity,
                            tolerations=self.tolerations,
                            init_containers=self.init_containers,
                            containers=[
                                k8s.V1Container(
                                    image=self.image,
                                    name="base",
                                    command=self.cmds,
                                    ports=self.ports,
                                    resources=self.k8s_resources,
                                    volume_mounts=self.volume_mounts,
                                    args=self.arguments,
                                    env=self.env_vars,
                                    env_from=self.env_from,
                                )
                            ],
                            image_pull_secrets=self.image_pull_secrets,
                            service_account_name=self.service_account_name,
                            host_network=self.hostnetwork,
                            security_context=self.security_context,
                            dns_policy=self.dnspolicy,
                            scheduler_name=self.schedulername,
                            restart_policy='Never',
                            priority_class_name=self.priority_class_name,
                            volumes=self.volumes,
                        ))

        pod = PodGenerator.reconcile_pods(pod_template, pod)

        for secret in self.secrets:
            self.log.debug("Adding secret to task %s", self.task_id)
            pod = secret.attach_to_pod(pod)
        if self.do_xcom_push:
            self.log.debug("Adding xcom sidecar to task %s", self.task_id)
            pod = PodGenerator.add_xcom_sidecar(pod)
        return pod
 def test_merge_objects(self):
     base_annotations = {'foo1': 'bar1'}
     base_labels = {'foo1': 'bar1'}
     client_annotations = {'foo2': 'bar2'}
     base_obj = k8s.V1ObjectMeta(annotations=base_annotations, labels=base_labels)
     client_obj = k8s.V1ObjectMeta(annotations=client_annotations)
     res = merge_objects(base_obj, client_obj)
     client_obj.labels = base_labels
     assert client_obj == res
Esempio n. 3
0
    def handleSecret(self, key: str, value: T.Dict[str, str],
                     full_path: str) -> None:
        if not self.jobSecret:
            assert self.object
            name = self.dnsify("jobsecret." + str(self.object.get("id", "")))
            metadata = K.V1ObjectMeta(name=name)
            assert self.manifest_list
            clusterMeta = self.manifest_list.clusterMeta()
            if clusterMeta:
                metadata.annotations = clusterMeta.annotations
            self.jobSecret = K.V1Secret(metadata=metadata, data={})
            self.jobSecret.api_version = 'v1'
            self.jobSecret.kind = 'Secret'
            self.manifest.append(self.jobSecret)

        sourceSecretName = value.get("source", "")

        if not sourceSecretName:
            # logging.critical("{}.source missing", full_path)
            return

        source_key = utils.dnsify(sourceSecretName)
        assert self.manifest_list
        sourceSecret = self.manifest_list.manifest(pluginName='secret',
                                                   manifestName=source_key)
        if not sourceSecret:
            # logging.warning("secret {} missing", source_key)
            self.jobSecret.data[key] = "{} not found".format(source_key)
            return

        [v] = sourceSecret[0].data.values()

        self.jobSecret.data[key] = v
Esempio n. 4
0
def build_pod_spec(name, bucket, data_dir):
    metadata = k8s.V1ObjectMeta(name=make_unique_pod_name(name), )
    container = k8s.V1Container(
        name=name,
        lifecycle=k8s.V1Lifecycle(
            post_start=k8s.V1Handler(_exec=k8s.V1ExecAction(command=[
                "gcsfuse",
                "--log-file",
                "/var/log/gcs_fuse.log",
                "--temp-dir",
                "/tmp",
                "--debug_gcs",
                bucket,
                data_dir,
            ])),
            pre_stop=k8s.V1Handler(_exec=k8s.V1ExecAction(
                command=["fusermount", "-u", data_dir])),
        ),
        security_context=k8s.V1SecurityContext(
            privileged=True,
            capabilities=k8s.V1Capabilities(add=["SYS_ADMIN"])),
    )
    pod = k8s.V1Pod(metadata=metadata,
                    spec=k8s.V1PodSpec(containers=[container]))
    return pod
Esempio n. 5
0
    def test_full_pod_spec(self):
        pod_spec = k8s.V1Pod(
            metadata=k8s.V1ObjectMeta(
                labels={"foo": "bar", "fizz": "buzz"}, namespace="default", name="test-pod"
            ),
            spec=k8s.V1PodSpec(
                containers=[
                    k8s.V1Container(
                        name="base",
                        image="perl",
                        command=["/bin/bash"],
                        args=["-c", 'echo {\\"hello\\" : \\"world\\"} | cat > /airflow/xcom/return.json'],
                        env=[k8s.V1EnvVar(name="env_name", value="value")],
                    )
                ],
                restart_policy="Never",
            ),
        )
        k = KubernetesPodOperator(
            task_id="task" + self.get_current_task_name(),
            in_cluster=False,
            full_pod_spec=pod_spec,
            do_xcom_push=True,
        )

        context = create_context(k)
        result = k.execute(context)
        self.assertIsNotNone(result)
        self.assertEqual(k.pod.metadata.labels, {'fizz': 'buzz', 'foo': 'bar'})
        self.assertEqual(k.pod.spec.containers[0].env, [k8s.V1EnvVar(name="env_name", value="value")])
        self.assertDictEqual(result, {"hello": "world"})
Esempio n. 6
0
    def test_adopt_launched_task(self, mock_kube_client):
        executor = self.kubernetes_executor
        executor.scheduler_job_id = "modified"
        annotations = {
            'dag_id': 'dag',
            'execution_date': datetime.utcnow().isoformat(),
            'task_id': 'task',
            'try_number': '1',
        }
        ti_key = annotations_to_key(annotations)
        pod = k8s.V1Pod(
            metadata=k8s.V1ObjectMeta(name="foo",
                                      labels={"airflow-worker": "bar"},
                                      annotations=annotations))
        pod_ids = {ti_key: {}}

        executor.adopt_launched_task(mock_kube_client,
                                     pod=pod,
                                     pod_ids=pod_ids)
        assert mock_kube_client.patch_namespaced_pod.call_args[1] == {
            'body': {
                'metadata': {
                    'labels': {
                        'airflow-worker': 'modified'
                    },
                    'annotations': annotations,
                    'name': 'foo',
                }
            },
            'name': 'foo',
            'namespace': None,
        }
        assert pod_ids == {}
        assert executor.running == {ti_key}
Esempio n. 7
0
    def initCronJob(self, key: str, value: str, full_path: str) -> None:
        name = self.dnsify(value)
        metadata = K.V1ObjectMeta(name=name)
        assert self.manifest_list
        clusterMeta = self.manifest_list.clusterMeta()
        if clusterMeta:
            metadata.annotations = clusterMeta.annotations

        # intentionally written this way so one can easily scan down paths
        container1 = K.V1Container(
            name="job",
            resources=K.V1ResourceRequirements(limits={}, requests={}),
        )
        self.manifest = Manifest(
            data=[
                K.V1beta1CronJob(
                    metadata=metadata,
                    kind="CronJob",
                    api_version="batch/v1beta1",
                    spec=K.V1beta1CronJobSpec(
                        schedule="* * * * *",
                        suspend=True,
                        job_template=K.V1beta1JobTemplateSpec(spec=K.V1JobSpec(
                            template=K.V1PodTemplateSpec(spec=K.V1PodSpec(
                                containers=[container1])))),
                    ),
                )
            ],
            pluginName="metronome",
            manifestName=name,
        )
Esempio n. 8
0
 def setUp(self):
     self.watcher = KubernetesJobWatcher(
         namespace="airflow",
         multi_namespace_mode=False,
         watcher_queue=mock.MagicMock(),
         resource_version="0",
         scheduler_job_id="123",
         kube_config=mock.MagicMock(),
     )
     self.kube_client = mock.MagicMock()
     self.core_annotations = {
         "dag_id": "dag",
         "task_id": "task",
         "execution_date": "dt",
         "try_number": "1",
     }
     self.pod = k8s.V1Pod(
         metadata=k8s.V1ObjectMeta(
             name="foo",
             annotations={
                 "airflow-worker": "bar",
                 **self.core_annotations
             },
             namespace="airflow",
             resource_version="456",
         ),
         status=k8s.V1PodStatus(phase="Pending"),
     )
     self.events = []
Esempio n. 9
0
    def test_encode_k8s_v1pod(self):
        from kubernetes.client import models as k8s

        pod = k8s.V1Pod(
            metadata=k8s.V1ObjectMeta(
                name="foo",
                namespace="bar",
            ),
            spec=k8s.V1PodSpec(
                containers=[k8s.V1Container(
                    name="foo",
                    image="bar",
                )]),
        )
        self.assertEqual(
            json.loads(json.dumps(pod, cls=utils_json.AirflowJsonEncoder)),
            {
                "metadata": {
                    "name": "foo",
                    "namespace": "bar"
                },
                "spec": {
                    "containers": [{
                        "image": "bar",
                        "name": "foo"
                    }]
                },
            },
        )
Esempio n. 10
0
    def translate_pool(self, key: str, value: str, full_path: str) -> None:
        assert self.object is not None
        objects = migrate(self.object)

        cluster_annotations = {}
        assert self.manifest_list is not None
        cluster_metadata = self.manifest_list.clusterMeta()
        if cluster_metadata is not None and cluster_metadata.annotations:
            cluster_annotations = cluster_metadata.annotations

        if not any(objects):
            return

        self.manifest = system.Manifest(
            pluginName="ingress",
            manifestName=self.dnsify(value),
        )

        assert self.manifest is not None
        for obj in objects:
            if not obj:
                continue

            if obj.metadata:
                obj.metadata.annotations.update(cluster_annotations)
            else:
                obj.metadata = models.V1ObjectMeta(
                    annotations=cluster_annotations, )

            self.manifest.append(obj)
Esempio n. 11
0
 def test_adopt_launched_task(self, mock_kube_client):
     executor = self.kubernetes_executor
     executor.scheduler_job_id = "modified"
     pod_ids = {"dagtask": {}}
     pod = k8s.V1Pod(metadata=k8s.V1ObjectMeta(name="foo",
                                               labels={
                                                   "airflow-worker": "bar",
                                                   "dag_id": "dag",
                                                   "task_id": "task"
                                               }))
     executor.adopt_launched_task(mock_kube_client,
                                  pod=pod,
                                  pod_ids=pod_ids)
     assert mock_kube_client.patch_namespaced_pod.call_args[1] == {
         'body': {
             'metadata': {
                 'labels': {
                     'airflow-worker': 'modified',
                     'dag_id': 'dag',
                     'task_id': 'task'
                 },
                 'name': 'foo',
             }
         },
         'name': 'foo',
         'namespace': None,
     }
     assert pod_ids == {}
Esempio n. 12
0
    def test_pod_template_file_with_full_pod_spec(self):
        fixture = sys.path[0] + '/tests/kubernetes/basic_pod.yaml'
        pod_spec = k8s.V1Pod(
            metadata=k8s.V1ObjectMeta(labels={
                "foo": "bar",
                "fizz": "buzz"
            }, ),
            spec=k8s.V1PodSpec(containers=[
                k8s.V1Container(
                    name="base",
                    env=[k8s.V1EnvVar(name="env_name", value="value")],
                )
            ]),
        )
        k = KubernetesPodOperator(
            task_id="task" + self.get_current_task_name(),
            in_cluster=False,
            pod_template_file=fixture,
            full_pod_spec=pod_spec,
            do_xcom_push=True,
        )

        context = create_context(k)
        result = k.execute(context)
        self.assertIsNotNone(result)
        self.assertEqual(k.pod.metadata.labels, {'fizz': 'buzz', 'foo': 'bar'})
        self.assertEqual(k.pod.spec.containers[0].env,
                         [k8s.V1EnvVar(name="env_name", value="value")])
        self.assertDictEqual(result, {"hello": "world"})
Esempio n. 13
0
    def test_pending_pod_timeout_multi_namespace_mode(
            self, mock_kubescheduler, mock_get_kube_client,
            mock_kubernetes_job_watcher):
        mock_delete_pod = mock_kubescheduler.return_value.delete_pod
        mock_kube_client = mock_get_kube_client.return_value
        now = timezone.utcnow()
        pending_pods = [
            k8s.V1Pod(metadata=k8s.V1ObjectMeta(
                name="foo90",
                labels={"airflow-worker": "123"},
                creation_timestamp=now - timedelta(seconds=500),
                namespace="anothernamespace",
            )),
        ]
        mock_kube_client.list_pod_for_all_namespaces.return_value.items = pending_pods

        config = {
            ('kubernetes', 'namespace'): 'mynamespace',
            ('kubernetes', 'multi_namespace_mode'): 'true',
            ('kubernetes', 'kube_client_request_args'): '{"sentinel": "foo"}',
        }
        with conf_vars(config):
            executor = KubernetesExecutor()
            executor.job_id = "123"
            executor.start()
            executor._check_worker_pods_pending_timeout()

        mock_kube_client.list_pod_for_all_namespaces.assert_called_once_with(
            field_selector='status.phase=Pending',
            label_selector='airflow-worker=123',
            limit=100,
            sentinel='foo',
        )
        mock_delete_pod.assert_called_once_with('foo90', 'anothernamespace')
Esempio n. 14
0
    def test_full_pod_spec(self, mock_client, monitor_mock, start_mock):
        from airflow.utils.state import State

        pod_spec = k8s.V1Pod(
            metadata=k8s.V1ObjectMeta(name="hello",
                                      labels={"foo": "bar"},
                                      namespace="mynamespace"),
            spec=k8s.V1PodSpec(containers=[
                k8s.V1Container(
                    name="base",
                    image="ubuntu:16.04",
                    command=["something"],
                )
            ]),
        )

        k = KubernetesPodOperator(
            task_id="task",
            in_cluster=False,
            do_xcom_push=False,
            cluster_context='default',
            full_pod_spec=pod_spec,
        )
        monitor_mock.return_value = (State.SUCCESS, None)
        context = self.create_context(k)
        k.execute(context=context)

        assert start_mock.call_args[0][
            0].metadata.name == pod_spec.metadata.name
        assert start_mock.call_args[0][
            0].metadata.labels == pod_spec.metadata.labels
        assert start_mock.call_args[0][
            0].metadata.namespace == pod_spec.metadata.namespace
        assert start_mock.call_args[0][0].spec.containers[
            0].image == pod_spec.spec.containers[0].image
        assert start_mock.call_args[0][0].spec.containers[
            0].command == pod_spec.spec.containers[0].command

        # kwargs take precedence, however
        start_mock.reset_mock()
        image = "some.custom.image:andtag"
        name_base = "world"
        k = KubernetesPodOperator(
            task_id="task",
            in_cluster=False,
            do_xcom_push=False,
            cluster_context='default',
            full_pod_spec=pod_spec,
            name=name_base,
            image=image,
        )
        context = self.create_context(k)
        k.execute(context=context)

        # make sure the kwargs takes precedence (and that name is randomized)
        assert start_mock.call_args[0][0].metadata.name.startswith(name_base)
        assert start_mock.call_args[0][0].metadata.name != name_base
        assert start_mock.call_args[0][0].spec.containers[0].image == image
Esempio n. 15
0
 def mock_iter_resp_lines(resp):
     for name in ['test1', 'test2', 'test3']:
         yield json.dumps({
             'type':
             'ADDED',
             'object':
             models.V1DeploymentConfig(metadata=k8s_models.V1ObjectMeta(
                 name=name, resource_version=1)).to_dict()
         })
    def test_reconcile_pods(self, mock_uuid):
        mock_uuid.return_value = self.static_uuid
        path = sys.path[
            0] + '/tests/kubernetes/pod_generator_base_with_secrets.yaml'

        base_pod = PodGenerator(pod_template_file=path,
                                extract_xcom=False).gen_pod()

        mutator_pod = k8s.V1Pod(
            metadata=k8s.V1ObjectMeta(
                name="name2",
                labels={"bar": "baz"},
            ),
            spec=k8s.V1PodSpec(
                containers=[
                    k8s.V1Container(
                        image='',
                        name='name',
                        command=['/bin/command2.sh', 'arg2'],
                        volume_mounts=[
                            k8s.V1VolumeMount(
                                mount_path="/foo/",
                                name="example-kubernetes-test-volume2")
                        ],
                    )
                ],
                volumes=[
                    k8s.V1Volume(
                        host_path=k8s.V1HostPathVolumeSource(path="/tmp/"),
                        name="example-kubernetes-test-volume2",
                    )
                ],
            ),
        )

        result = PodGenerator.reconcile_pods(base_pod, mutator_pod)
        expected: k8s.V1Pod = self.expected
        expected.metadata.name = "name2"
        expected.metadata.labels['bar'] = 'baz'
        expected.spec.volumes = expected.spec.volumes or []
        expected.spec.volumes.append(
            k8s.V1Volume(host_path=k8s.V1HostPathVolumeSource(path="/tmp/"),
                         name="example-kubernetes-test-volume2"))

        base_container: k8s.V1Container = expected.spec.containers[0]
        base_container.command = ['/bin/command2.sh', 'arg2']
        base_container.volume_mounts = [
            k8s.V1VolumeMount(mount_path="/foo/",
                              name="example-kubernetes-test-volume2")
        ]
        base_container.name = "name"
        expected.spec.containers[0] = base_container

        result_dict = self.k8s_client.sanitize_for_serialization(result)
        expected_dict = self.k8s_client.sanitize_for_serialization(expected)

        assert result_dict == expected_dict
    def test_merge_objects_empty(self):
        annotations = {'foo1': 'bar1'}
        base_obj = k8s.V1ObjectMeta(annotations=annotations)
        client_obj = None
        res = merge_objects(base_obj, client_obj)
        assert base_obj == res

        client_obj = k8s.V1ObjectMeta()
        res = merge_objects(base_obj, client_obj)
        assert base_obj == res

        client_obj = k8s.V1ObjectMeta(annotations=annotations)
        base_obj = None
        res = merge_objects(base_obj, client_obj)
        assert client_obj == res

        base_obj = k8s.V1ObjectMeta()
        res = merge_objects(base_obj, client_obj)
        assert client_obj == res
    def test_merge_objects_empty(self):
        annotations = {'foo1': 'bar1'}
        base_obj = k8s.V1ObjectMeta(annotations=annotations)
        client_obj = None
        res = merge_objects(base_obj, client_obj)
        self.assertEqual(base_obj, res)

        client_obj = k8s.V1ObjectMeta()
        res = merge_objects(base_obj, client_obj)
        self.assertEqual(base_obj, res)

        client_obj = k8s.V1ObjectMeta(annotations=annotations)
        base_obj = None
        res = merge_objects(base_obj, client_obj)
        self.assertEqual(client_obj, res)

        base_obj = k8s.V1ObjectMeta()
        res = merge_objects(base_obj, client_obj)
        self.assertEqual(client_obj, res)
Esempio n. 19
0
    def test_try_adopt_task_instances(self, mock_adopt_completed_pods,
                                      mock_adopt_launched_task):
        executor = self.kubernetes_executor
        executor.scheduler_job_id = "10"
        ti_key = annotations_to_key({
            'dag_id':
            'dag',
            'execution_date':
            datetime.utcnow().isoformat(),
            'task_id':
            'task',
            'try_number':
            '1',
        })
        mock_ti = mock.MagicMock(queued_by_job_id="1",
                                 external_executor_id="1",
                                 key=ti_key)
        pod = k8s.V1Pod(metadata=k8s.V1ObjectMeta(name="foo"))
        mock_kube_client = mock.MagicMock()
        mock_kube_client.list_namespaced_pod.return_value.items = [pod]
        executor.kube_client = mock_kube_client

        # First adoption
        reset_tis = executor.try_adopt_task_instances([mock_ti])
        mock_kube_client.list_namespaced_pod.assert_called_once_with(
            namespace='default', label_selector='airflow-worker=1')
        mock_adopt_launched_task.assert_called_once_with(
            mock_kube_client, pod, {ti_key: mock_ti})
        mock_adopt_completed_pods.assert_called_once()
        assert reset_tis == [mock_ti
                             ]  # assume failure adopting when checking return

        # Second adoption (queued_by_job_id and external_executor_id no longer match)
        mock_kube_client.reset_mock()
        mock_adopt_launched_task.reset_mock()
        mock_adopt_completed_pods.reset_mock()

        mock_ti.queued_by_job_id = "10"  # scheduler_job would have updated this after the first adoption
        executor.scheduler_job_id = "20"
        # assume success adopting when checking return, `adopt_launched_task` pops `ti_key` from `pod_ids`
        mock_adopt_launched_task.side_effect = lambda client, pod, pod_ids: pod_ids.pop(
            ti_key)

        reset_tis = executor.try_adopt_task_instances([mock_ti])
        mock_kube_client.list_namespaced_pod.assert_called_once_with(
            namespace='default', label_selector='airflow-worker=10')
        mock_adopt_launched_task.assert_called_once(
        )  # Won't check args this time around as they get mutated
        mock_adopt_completed_pods.assert_called_once()
        assert reset_tis == [
        ]  # This time our return is empty - no TIs to reset
Esempio n. 20
0
    def construct_pod(  # pylint: disable=too-many-arguments
            dag_id: str, task_id: str, pod_id: str, try_number: int,
            kube_image: str, date: datetime.datetime, command: List[str],
            pod_override_object: Optional[k8s.V1Pod],
            base_worker_pod: k8s.V1Pod, namespace: str,
            worker_uuid: str) -> k8s.V1Pod:
        """
        Construct a pod by gathering and consolidating the configuration from 3 places:
            - airflow.cfg
            - executor_config
            - dynamic arguments
        """
        try:
            image = pod_override_object.spec.containers[
                0].image  # type: ignore
            if not image:
                image = kube_image
        except Exception:  # pylint: disable=W0703
            image = kube_image

        dynamic_pod = k8s.V1Pod(metadata=k8s.V1ObjectMeta(
            namespace=namespace,
            annotations={
                'dag_id': dag_id,
                'task_id': task_id,
                'execution_date': date.isoformat(),
                'try_number': str(try_number),
            },
            name=PodGenerator.make_unique_pod_id(pod_id),
            labels={
                'airflow-worker': worker_uuid,
                'dag_id': dag_id,
                'task_id': task_id,
                'execution_date': datetime_to_label_safe_datestring(date),
                'try_number': str(try_number),
                'airflow_version': airflow_version.replace('+', '-'),
                'kubernetes_executor': 'True',
            }),
                                spec=k8s.V1PodSpec(containers=[
                                    k8s.V1Container(
                                        name="base",
                                        command=command,
                                        image=image,
                                    )
                                ]))

        # Reconcile the pods starting with the first chronologically,
        # Pod from the pod_template_File -> Pod from executor_config arg -> Pod from the K8s executor
        pod_list = [base_worker_pod, pod_override_object, dynamic_pod]

        return reduce(PodGenerator.reconcile_pods, pod_list)
    def to_v1_kubernetes_pod(self):
        """
        Convert to support k8s V1Pod

        :return: k8s.V1Pod
        """
        import kubernetes.client.models as k8s
        meta = k8s.V1ObjectMeta(
            labels=self.labels,
            name=self.name,
            namespace=self.namespace,
        )
        spec = k8s.V1PodSpec(
            init_containers=self.init_containers,
            containers=[
                k8s.V1Container(
                    image=self.image,
                    command=self.cmds,
                    name="base",
                    env=[k8s.V1EnvVar(name=key, value=val) for key, val in self.envs.items()],
                    args=self.args,
                    image_pull_policy=self.image_pull_policy,
                )
            ],
            image_pull_secrets=self.image_pull_secrets,
            service_account_name=self.service_account_name,
            dns_policy=self.dnspolicy,
            host_network=self.hostnetwork,
            tolerations=self.tolerations,
            affinity=self.affinity,
            security_context=self.security_context,
        )

        pod = k8s.V1Pod(
            spec=spec,
            metadata=meta,
        )
        for port in _extract_ports(self.ports):
            pod = port.attach_to_pod(pod)
        volumes = _extract_volumes(self.volumes)
        for volume in volumes:
            pod = volume.attach_to_pod(pod)
        for volume_mount in _extract_volume_mounts(self.volume_mounts):
            pod = volume_mount.attach_to_pod(pod)
        for secret in self.secrets:
            pod = secret.attach_to_pod(pod)
        for runtime_info in self.pod_runtime_info_envs:
            pod = runtime_info.attach_to_pod(pod)
        pod = _extract_resources(self.resources).attach_to_pod(pod)
        return pod
Esempio n. 22
0
    def test_full_pod_spec(self):
        pod_spec = k8s.V1Pod(
            metadata=k8s.V1ObjectMeta(labels={
                "foo": "bar",
                "fizz": "buzz"
            },
                                      namespace="default",
                                      name="test-pod"),
            spec=k8s.V1PodSpec(
                containers=[
                    k8s.V1Container(
                        name="base",
                        image="perl",
                        command=["/bin/bash"],
                        args=[
                            "-c",
                            'echo {\\"hello\\" : \\"world\\"} | cat > /airflow/xcom/return.json'
                        ],
                        env=[k8s.V1EnvVar(name="env_name", value="value")],
                    )
                ],
                restart_policy="Never",
            ),
        )
        k = KubernetesPodOperator(
            task_id="task" + self.get_current_task_name(),
            in_cluster=False,
            full_pod_spec=pod_spec,
            do_xcom_push=True,
            is_delete_operator_pod=False,
        )

        context = create_context(k)
        result = k.execute(context)
        assert result is not None
        assert k.pod.metadata.labels == {
            'fizz': 'buzz',
            'foo': 'bar',
            'airflow_version': mock.ANY,
            'dag_id': 'dag',
            'run_id': 'manual__2016-01-01T0100000100-da4d1ce7b',
            'kubernetes_pod_operator': 'True',
            'task_id': mock.ANY,
            'try_number': '1',
            'already_checked': 'True',
        }
        assert k.pod.spec.containers[0].env == [
            k8s.V1EnvVar(name="env_name", value="value")
        ]
        assert result == {"hello": "world"}
Esempio n. 23
0
    def test_full_pod_spec(self):
        pod_spec = k8s.V1Pod(
            metadata=k8s.V1ObjectMeta(name="hello",
                                      labels={"foo": "bar"},
                                      namespace="mynamespace"),
            spec=k8s.V1PodSpec(containers=[
                k8s.V1Container(
                    name="base",
                    image="ubuntu:16.04",
                    command=["something"],
                )
            ]),
        )

        k = KubernetesPodOperator(
            task_id="task",
            in_cluster=False,
            do_xcom_push=False,
            cluster_context="default",
            full_pod_spec=pod_spec,
        )
        pod = k.create_pod_request_obj()

        assert pod.metadata.name == pod_spec.metadata.name
        assert pod.metadata.labels == pod_spec.metadata.labels
        assert pod.metadata.namespace == pod_spec.metadata.namespace
        assert pod.spec.containers[0].image == pod_spec.spec.containers[
            0].image
        assert pod.spec.containers[0].command == pod_spec.spec.containers[
            0].command

        # kwargs take precedence, however
        image = "some.custom.image:andtag"
        name_base = "world"
        k = KubernetesPodOperator(
            task_id="task",
            in_cluster=False,
            do_xcom_push=False,
            cluster_context="default",
            full_pod_spec=pod_spec,
            name=name_base,
            image=image,
        )
        pod = k.create_pod_request_obj()

        # make sure the kwargs takes precedence (and that name is randomized)
        assert pod.metadata.name.startswith(name_base)
        assert pod.metadata.name != name_base
        assert pod.spec.containers[0].image == image
Esempio n. 24
0
 def ensure_kubernetes_namespace(self):
     kubernetes_client = KubernetesClient()
     try:
         kubernetes_client.core.read_namespace(
             self.job_resources["namespace"])
     except kubernetes.client.exceptions.ApiException as e:
         if e.status != 404:
             raise e
         kubernetes_client.core.create_namespace(
             body=kubernetes_models.V1Namespace(
                 api_version="v1",
                 kind="Namespace",
                 metadata=kubernetes_models.V1ObjectMeta(
                     name=self.job_resources["namespace"]),
             ))
 def create_pvc(self):
     """
     Creates k8s pvc definition object
     :return:
     """
     pvc = k8s.V1PersistentVolumeClaim(
         metadata=k8s.V1ObjectMeta(name=self.pvc_name,
                                   namespace=self._namespace),
         spec=k8s.V1PersistentVolumeClaimSpec(
             access_modes=self._access_modes,
             storage_class_name=self._storage_class_name,
             resources=k8s.V1ResourceRequirements(
                 requests={"storage": self._volumes_size}),
         ),
     )
     return pvc
Esempio n. 26
0
def migrate_lb(pool: Dict[str, Any]) -> List[models.V1Service]:
    output = []

    backends = pool.get("backends", {})
    frontends = pool.get("frontends", {})

    for frontend in frontends.values():
        if "HTTP" in frontend.get("protocol").upper():
            continue

        backend = backends.get(frontend.get("default_backend"))

        if not backend:
            warnings.warn(
                TCP_BACKEND_WARNING.format(frontend.get("name", "UNKNOWN")))
            continue

        # TODO(jkoelker) figure out targetPort
        port = models.V1ServicePort(
            port=frontend["port"],
            target_port=0,
            protocol=frontend.get("protocol").upper(),
        )
        spec = models.V1ServiceSpec(
            type="LoadBalancer",
            ports=[port],
            selector={
                "app": backend["service"]["name"],
            },
        )

        metadata = models.V1ObjectMeta(
            annotations={},
            name=pool["name"],
            namespace=pool.get("namespace"),
        )

        lb = models.V1Service(
            api_version="v1",
            kind="Service",
            metadata=metadata,
            spec=spec,
        )

        output.append(lb)

    return output
Esempio n. 27
0
    def test_not_adopt_unassigned_task(self, mock_kube_client):
        """
        We should not adopt any tasks that were not assigned by the scheduler.
        This ensures that there is no contention over pod management.
        """

        executor = self.kubernetes_executor
        executor.scheduler_job_id = "modified"
        pod_ids = {"foobar": {}}
        pod = k8s.V1Pod(
            metadata=k8s.V1ObjectMeta(
                name="foo", labels={"airflow-worker": "bar", "dag_id": "dag", "task_id": "task"}
            )
        )
        executor.adopt_launched_task(mock_kube_client, pod=pod, pod_ids=pod_ids)
        assert not mock_kube_client.patch_namespaced_pod.called
        assert pod_ids == {"foobar": {}}
    def test_add_custom_label(self):
        from kubernetes.client import models as k8s

        pod = PodGenerator.construct_pod(
            namespace="test",
            worker_uuid="test",
            pod_id="test",
            dag_id="test",
            task_id="test",
            try_number=1,
            date="23-07-2020",
            command="test",
            kube_executor_config=None,
            worker_config=k8s.V1Pod(metadata=k8s.V1ObjectMeta(
                labels={"airflow-test": "airflow-task-pod"},
                annotations={"my.annotation": "foo"})))
        self.assertIn("airflow-test", pod.metadata.labels)
        self.assertIn("my.annotation", pod.metadata.annotations)
Esempio n. 29
0
    def test_try_adopt_task_instances(self, mock_adopt_completed_pods,
                                      mock_adopt_launched_task):
        executor = self.kubernetes_executor
        executor.scheduler_job_id = "10"
        mock_ti = mock.MagicMock(queued_by_job_id="1",
                                 external_executor_id="1",
                                 dag_id="dag",
                                 task_id="task")
        pod = k8s.V1Pod(metadata=k8s.V1ObjectMeta(name="foo",
                                                  labels={
                                                      "dag_id": "dag",
                                                      "task_id": "task"
                                                  }))
        pod_id = create_pod_id(dag_id="dag", task_id="task")
        mock_kube_client = mock.MagicMock()
        mock_kube_client.list_namespaced_pod.return_value.items = [pod]
        executor.kube_client = mock_kube_client

        # First adoption
        executor.try_adopt_task_instances([mock_ti])
        mock_kube_client.list_namespaced_pod.assert_called_once_with(
            namespace='default', label_selector='airflow-worker=1')
        mock_adopt_launched_task.assert_called_once_with(
            mock_kube_client, pod, {pod_id: mock_ti})
        mock_adopt_completed_pods.assert_called_once()
        # We aren't checking the return value of `try_adopt_task_instances` because it relies on
        # `adopt_launched_task` mutating its arg. This should be refactored, but not right now.

        # Second adoption (queued_by_job_id and external_executor_id no longer match)
        mock_kube_client.reset_mock()
        mock_adopt_launched_task.reset_mock()
        mock_adopt_completed_pods.reset_mock()

        mock_ti.queued_by_job_id = "10"  # scheduler_job would have updated this after the first adoption
        executor.scheduler_job_id = "20"

        executor.try_adopt_task_instances([mock_ti])
        mock_kube_client.list_namespaced_pod.assert_called_once_with(
            namespace='default', label_selector='airflow-worker=10')
        mock_adopt_launched_task.assert_called_once_with(
            mock_kube_client, pod, {pod_id: mock_ti})
        mock_adopt_completed_pods.assert_called_once()
Esempio n. 30
0
def mock_kubernetes_read_namespaced_pod(*_args, **_kwargs):
    """
    Represents the mocked output of kubernetes.client.read_namespaced_pod
    """
    return models.V1Pod(
        metadata=models.V1ObjectMeta(
            namespace="default",
            name="gordo-test-pod-name-1234",
            labels={"app": "gordo-model-builder"},
        ),
        status=models.V1PodStatus(phase="Running"),
        spec=models.V1PodSpec(containers=[
            models.V1Container(
                name="some-generated-test-container-name",
                env=[
                    models.V1EnvVar(name="MACHINE_NAME",
                                    value="test-machine-name")
                ],
            )
        ]),
    )