Skip to content

Commit

Permalink
Added k9s as integrated tool to help with kubernetes testing (#12163)
Browse files Browse the repository at this point in the history
The K9s is fantastic tool that helps to debug a running k8s
instance. It is terminal-based windowed CLI that makes you
several times more productive comparing to using kubectl
commands. We've integrated k9s (it is run as a docker container
and downloaded on demand). We've also separated out KUBECONFIG
of the integrated kind cluster so that it does not mess with
kubernetes configuration you might already have.

Also - together with that the "surrounding" of the kubernetes
tests were simplified and improved so that the k9s integration
can be utilized well. Instead of kubectl port forwarding (which
caused multitude of problems) we are now utilizing kind's
portMapping feature + custom NodePort resource that maps
port 8080 to 30007 NodePort which in turn maps it to 8080
port of the Webserver. This way we do not have to establish
an external kubectl port forward which is prone to error and
management - everything is brought up when Airflow gets
deployed to the Kind Cluster and shuts down when the Kind
cluster is stopped.

Yet another problem fixed was killing of postgres by one of the
kubernetes tests ('test_integration_run_dag_with_scheduler_failure').
Instead of just killing the scheduler it killed all pods - including
the Postgres one (it was named 'airflow-postgres.*'). That caused
various problems, as the database could be left in a strange state.
I changed the tests to do what it claimed was doing - so killing only the
scheduler during the test. This seemed to improve the stability
of tests immensely in my local setup.

(cherry picked from commit 21999dd)
  • Loading branch information
potiuk authored and kaxil committed Nov 18, 2020
1 parent 3b5d6ec commit ac7f97d
Show file tree
Hide file tree
Showing 18 changed files with 299 additions and 107 deletions.
17 changes: 4 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -636,23 +636,14 @@ jobs:
python-version: ${{ env.PYTHON_MAJOR_MINOR_VERSION }}
- name: "Free space"
run: ./scripts/ci/tools/ci_free_space_on_ci.sh
- name: "Setup Kind Cluster ${{ env.KIND_VERSION }}"
uses: engineerd/setup-kind@v0.4.0
with:
version: "${{ env.KIND_VERSION }}"
name: airflow-python-${{matrix.python-version}}-${{matrix.kubernetes-version}}
config: "scripts/ci/kubernetes/kind-cluster-conf.yaml"
- name: "Prepare PROD Image"
run: ./scripts/ci/images/ci_prepare_prod_image_on_ci.sh
- name: "Deploy airflow to cluster"
id: deploy-app
run: ./scripts/ci/kubernetes/ci_deploy_app_to_kubernetes.sh
- name: "Setup cluster and deploy Airflow"
id: setp-cluster-deploy-app
run: ./scripts/ci/kubernetes/ci_setup_cluster_and_deploy_airflow_to_kubernetes.sh
env:
# We have the right image pulled already by the previous step
SKIP_BUILDING_PROD_IMAGE: "true"
# due to some instabilities, in CI we try to increase port numbers when trying to establish
# port forwarding
INCREASE_PORT_NUMBER_FOR_KUBERNETES: "true"
- name: "Cache virtualenv for kubernetes testing"
uses: actions/cache@v2
env:
Expand All @@ -669,7 +660,7 @@ jobs:
key: "${{ env.cache-name }}-${{ github.job }}-${{ hashFiles('setup.py') }}\
-${{ needs.build-info.outputs.defaultKindVersion }}\
-${{ needs.build-info.outputs.defaultHelmVersion }}\
-$${{ matrix.kubernetes-version }}"
-${{ matrix.kubernetes-version }}"
- name: "Kubernetes Tests"
run: ./scripts/ci/kubernetes/ci_run_kubernetes_tests.sh
- name: "Upload KinD logs"
Expand Down
11 changes: 10 additions & 1 deletion BREEZE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,7 @@ This is the current syntax for `./breeze <./breeze>`_:
image building time in production image and at container entering time for CI image. One of:
1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2
wheel
-t, --install-airflow-reference INSTALL_AIRFLOW_REFERENCE
If specified, installs Airflow directly from reference in GitHub. This happens at
Expand Down Expand Up @@ -1712,7 +1713,14 @@ This is the current syntax for `./breeze <./breeze>`_:
to the cluster so you can also pass appropriate build image flags that will influence
rebuilding the production image. Operation is one of:
start stop restart status deploy test shell
start stop restart status deploy test shell k9s
The last two operations - shell and k9s allow you to perform interactive testing with
kubernetes tests. You can enter the shell from which you can run kubernetes tests and in
another terminal you can start the k9s CLI to debug kubernetes instance. It is an easy
way to debug the kubernetes deployments.
You can read more about k9s at https://k9scli.io/
Flags:
Expand Down Expand Up @@ -2087,6 +2095,7 @@ This is the current syntax for `./breeze <./breeze>`_:
image building time in production image and at container entering time for CI image. One of:
1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2
wheel
-t, --install-airflow-reference INSTALL_AIRFLOW_REFERENCE
If specified, installs Airflow directly from reference in GitHub. This happens at
Expand Down
151 changes: 140 additions & 11 deletions TESTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -418,14 +418,22 @@ can also decide to only run tests with ``-m quarantined`` flag to run only those
Running Tests with Kubernetes
=============================

Airflow has tests that are run against real kubernetes cluster. We are using
`Kind <https://kind.sigs.k8s.io/>`_ to create and run the cluster. We integrated the tools to start/stop/
deploy and run the cluster tests in our repository and into Breeze development environment.

Configuration for the cluster is kept in ``./build/.kube/config`` file in your Airflow source repository and
our scripts set the ``KUBECONFIG`` variable to it. If you want to interact with the Kind cluster created
you can do it from outside of the scripts by exporting this variable and point it to this file.

Starting Kubernetes Cluster
---------------------------

For your testing you manage Kind cluster with ``kind-cluster`` breeze command:

.. code-block:: bash
./breeze kind-cluster [ start | stop | recreate | status | deploy | test | shell ]
./breeze kind-cluster [ start | stop | recreate | status | deploy | test | shell | k9s ]
The command allows you to start/stop/recreate/status Kind Kubernetes cluster, deploy Airflow via Helm
chart as well as interact with the cluster (via test and shell commands).
Expand All @@ -444,11 +452,11 @@ Deploying Airflow to Kubernetes Cluster

Deploying Airflow to the Kubernetes cluster created is also done via ``kind-cluster deploy`` breeze command:

.. code-block:: bash`
.. code-block:: bash
./breeze kind-cluster deploy
The deploy commands performs tthose steps:
The deploy commands performs those steps:

1. It rebuilds the latest ``apache/airflow:master-pythonX.Y`` production images using the
latest sources using local cachine. It also adds example DAGs to the image, so that they do not
Expand All @@ -465,20 +473,63 @@ Running tests with Kubernetes Cluster
You can either run all tests or you can select which tests to run. You can also enter interactive virtualenv
to run the tests manually one by one.

.. code-block:: bash
Running kubernetes tests via shell:

Running kubernetes tests
.. code-block:: bash
./scripts/ci/kubernetes/ci_run_kubernetes_tests.sh - runs all kubernetes tests
./scripts/ci/kubernetes/ci_run_kubernetes_tests.sh TEST [TEST ...] - runs selected kubernetes tests (from kubernetes_tests folder)
Running kubernetes tests via breeze:

.. code-block:: bash
./breeze kind-cluster test
./breeze kind-cluster test -- TEST TEST [TEST ...]
Entering shell with Kubernetes Cluster
--------------------------------------

This shell is prepared to run kubernetes tests interactively. It has ``kubectl`` and ``kind`` cli tools
available in the path, it has also activated virtualenv environment that allows you to run tests via pytest.

You can enter the shell via those scripts

./scripts/ci/kubernetes/ci_run_kubernetes_tests.sh [-i|--interactive] - Activates virtual environment ready to run tests and drops you in
./scripts/ci/kubernetes/ci_run_kubernetes_tests.sh [--help] - Prints this help message


You can also run the same tests command with Breeze, using ``kind-cluster test`` command (to run all
kubernetes tests) and with ``kind-cluster shell`` command you can enter interactive shell when you can
run tests.
.. code-block:: bash
./breeze kind-cluster shell
K9s CLI - debug kubernetes in style!
------------------------------------

Breeze has built-in integration with fantastic k9s CLI tool, that allows you to debug the kubernetes
installation effortlessly and in style. K9S provides terminal (but windowed) CLI that allows you to
easily observe what's going on in the kubernetes instance, observe the resources defined (pods, secrets,
custom resource definitions), enter shell for the Pods/Containers running, see the log files and more.

You can read more about k9s at `https://k9scli.io/ <https://k9scli.io/>`_

Here is the screenshot of k9s tools in operation:

.. image:: images/testing/k9s.png
:align: center
:alt: K9S tool


You can enter the k9s tool via breeze (after you deployed Airflow):

.. code-block:: bash
./breeze kind-cluster k9s
You can exit k9s by pressing Ctrl-C.

Typical testing pattern for Kubernetes tests
--------------------------------------------
Expand Down Expand Up @@ -578,7 +629,6 @@ This prepares and enters the virtualenv in ``.build/.kubernetes_venv`` folder:
./breeze kind-cluster shell
Once you enter the environment you receive this information:


Expand All @@ -595,12 +645,67 @@ Once you enter the environment you receive this information:
You are entering the virtualenv now. Type exit to exit back to the original shell
In a separate terminal you can open the k9s CLI:

.. code-block:: bash
./breeze kind-cluster k9s
Use it to observe what's going on in your cluster.

6. Debugging in IntelliJ/PyCharm

It is very easy to running/debug Kubernetes tests with IntelliJ/PyCharm. Unlike the regular tests they are
in ``kubernetes_tests`` folder and if you followed the previous steps and entered the shell using
``./breeze kind-cluster shell`` command, you can setup your IDE very easily to run (and debug) your
tests using the standard IntelliJ Run/Debug feature. You just need a few steps:

a) Add the virtualenv as interpreter for the project:

.. image:: images/testing/kubernetes-virtualenv.png
:align: center
:alt: Kubernetes testing virtualenv

The virtualenv is created in your "Airflow" source directory in ``.build/.kubernetes_venv/`` folder and you
have to find ``python`` binary and choose it when selecting interpreter.

b) Choose pytest as test runner:

.. image:: images/testing/pytest-runner.png
:align: center
:alt: Pytest runner

c) Run/Debug tests using standard "Run/Debug" feature of IntelliJ

.. image:: images/testing/run-tests.png
:align: center
:alt: Run/Debug tests


NOTE! The first time you run it, it will likely fail with
``kubernetes.config.config_exception.ConfigException``:
``Invalid kube-config file. Expected key current-context in kube-config``. You need to add KUBECONFIG
environment variabl copying it from the result of "./breeze kind-cluster test":

.. code-block:: bash
echo ${KUBECONFIG}
/home/jarek/code/airflow/.build/.kube/config
.. image:: images/testing/kubeconfig-env.png
:align: center
:alt: Run/Debug tests


The configuration for kubernetes is stored in your "Airflow" source directory in ".build/.kube/config" file
and this is where KUBECONFIG env should point to.

You can iterate with tests while you are in the virtualenv. All the tests requiring kubernetes cluster
are in "kubernetes_tests" folder. You can add extra ``pytest`` parameters then (for example ``-s`` will
print output generated test logs and print statements to the terminal immediately.


.. code-block:: bash
pytest kubernetes_tests/test_kubernetes_executor.py::TestKubernetesExecutor::test_integration_run_dag_with_scheduler_failure -s
Expand All @@ -609,6 +714,30 @@ print output generated test logs and print statements to the terminal immediatel
You can modify the tests or KubernetesPodOperator and re-run them without re-deploying
airflow to KinD cluster.


Sometimes there are side effects from running tests. You can run ``redeploy_airflow.sh`` without
recreating the whole cluster. This will delete the whole namespace, including the database data
and start a new Airflow deployment in the cluster.

.. code-block:: bash
./scripts/ci/redeploy_airflow.sh
If needed you can also delete the cluster manually:


.. code-block:: bash
kind get clusters
kind delete clusters <NAME_OF_THE_CLUSTER>
Kind has also useful commands to inspect your running cluster:

.. code-block:: text
kind --help
However, when you change Airflow Kubernetes executor implementation you need to redeploy
Airflow to the cluster.

Expand All @@ -617,7 +746,7 @@ Airflow to the cluster.
./breeze kind-cluster deploy
5. Stop KinD cluster when you are done
7. Stop KinD cluster when you are done

.. code-block:: bash
Expand Down
9 changes: 9 additions & 0 deletions breeze
Original file line number Diff line number Diff line change
Expand Up @@ -1684,6 +1684,13 @@ ${CMDNAME} kind-cluster [FLAGS] OPERATION
${FORMATTED_KIND_OPERATIONS}
The last two operations - shell and k9s allow you to perform interactive testing with
kubernetes tests. You can enter the shell from which you can run kubernetes tests and in
another terminal you can start the k9s CLI to debug kubernetes instance. It is an easy
way to debug the kubernetes deployments.
You can read more about k9s at https://k9scli.io/
Flags:
$(breeze::flag_airflow_variants)
$(breeze::flag_build_docker_images)
Expand Down Expand Up @@ -2901,6 +2908,8 @@ function breeze::run_build_command() {
echo "Run Kubernetes tests with the KinD cluster "
elif [[ ${KIND_CLUSTER_OPERATION} == "shell" ]]; then
echo "Enter an interactive shell for kubernetes testing"
elif [[ ${KIND_CLUSTER_OPERATION} == "k9s" ]]; then
echo "Run k9s cli to debug in style"
elif [[ -z ${KIND_CLUSTER_OPERATION=} ]]; then
echo
echo "Please provide an operation to run"
Expand Down
5 changes: 3 additions & 2 deletions breeze-complete
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ _breeze_allowed_helm_versions="v3.2.4"
_breeze_allowed_kind_versions="v0.8.0"
_breeze_allowed_mysql_versions="5.6 5.7"
_breeze_allowed_postgres_versions="9.6 10 11 12 13"
_breeze_allowed_kind_operations="start stop restart status deploy test shell"
_breeze_allowed_kind_operations="start stop restart status deploy test shell k9s"
_breeze_allowed_test_types="All Core Integration Heisentests Postgres MySQL Helm"

# shellcheck disable=SC2034
Expand Down Expand Up @@ -60,6 +60,7 @@ _breeze_allowed_install_airflow_versions=$(cat <<-EOF
1.10.4
1.10.3
1.10.2
wheel
EOF
)

Expand Down Expand Up @@ -134,7 +135,7 @@ _breeze_long_options="
help python: backend: integration:
kubernetes-mode: kubernetes-version: helm-version: kind-version:
skip-mounting-local-sources install-airflow-version: install-airflow-reference: db-reset
verbose assume-yes assume-no assume-quit forward-credentials rbac-ui init-script:
verbose assume-yes assume-no assume-quit forward-credentials init-script:
force-build-images force-pull-images production-image extras: force-clean-images skip-rebuild-check
build-cache-local build-cache-pulled build-cache-disabled disable-pip-cache
dockerhub-user: dockerhub-repo: github-registry github-repository: github-image-id:
Expand Down
4 changes: 2 additions & 2 deletions chart/requirements.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ dependencies:
- name: postgresql
repository: https://charts.helm.sh/stable/
version: 6.3.12
digest: sha256:58d88cf56e78b2380091e9e16cc6ccf58b88b3abe4a1886dd47cd9faef5309af
generated: "2020-11-04T15:59:36.967913-08:00"
digest: sha256:1748aa702050d4e72ffba1b18960f49bfe5368757cf976116afeffbdedda1c98
generated: "2020-11-07T17:40:45.418723358+01:00"
Binary file added images/testing/k9s.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/testing/kubeconfig-env.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/testing/kubernetes-virtualenv.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/testing/pytest-runner.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/testing/run-test.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 7 additions & 2 deletions kubernetes_tests/test_kubernetes_pod_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ def create_context(task):
}


def get_kubeconfig_path():
kubeconfig_path = os.environ.get('KUBECONFIG')
return kubeconfig_path if kubeconfig_path else os.path.expanduser('~/.kube/config')


# noinspection DuplicatedCode,PyUnusedLocal
class TestKubernetesPodOperatorSystem(unittest.TestCase):
def get_current_task_name(self):
Expand Down Expand Up @@ -128,7 +133,7 @@ def create_context(self, task):

def test_do_xcom_push_defaults_false(self):
new_config_path = '/tmp/kube_config'
old_config_path = os.path.expanduser('~/.kube/config')
old_config_path = get_kubeconfig_path()
shutil.copy(old_config_path, new_config_path)

k = KubernetesPodOperator(
Expand All @@ -147,7 +152,7 @@ def test_do_xcom_push_defaults_false(self):

def test_config_path_move(self):
new_config_path = '/tmp/kube_config'
old_config_path = os.path.expanduser('~/.kube/config')
old_config_path = get_kubeconfig_path()
shutil.copy(old_config_path, new_config_path)

k = KubernetesPodOperator(
Expand Down

0 comments on commit ac7f97d

Please sign in to comment.