diff --git a/.python-version b/.python-version new file mode 100644 index 00000000..95ed564f --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.14.2 diff --git a/README.md b/README.md index 23720b60..3dc906ba 100644 --- a/README.md +++ b/README.md @@ -208,6 +208,76 @@ extraObjects: restartPolicy: OnFailure ``` +## Local Development + +### Prerequisites + +- **[uv](https://docs.astral.sh/uv/)**: Fast Python package installer and resolver +- **Docker**: Required for running Kubernetes in Docker (kind) during local testing +- **helm**: Required for chart dependencies and templating + +### Setting Up Your Development Environment + +1. Install uv and python + +```bash +# Install uv (if not already installed) +curl -LsSf https://astral.sh/uv/install.sh | sh + +# Install the Python version specified in .python-version +uv python install +``` + +2. Create and activate a virtual environment: + +```bash +uv venv +source .venv/bin/activate +``` + +3. Install dependencies: + +```bash +uv sync +``` + +4. Install pre-commit hooks: + +```bash +pre-commit install +``` + +5. Update Helm chart dependencies: + +```bash +helm dep update +``` + +### Running Tests + +The project uses `pytest` for testing the Helm chart. Tests are located in the `tests/` directory. + +#### Run pytests: + +```bash + +# Run specific test file +pytest tests/chart/test_ingress.py + +# Run tests matching a pattern +pytest "tests/chart/test_ingress.py::TestIngress::test_airflow_ingress_class_name_with_dag_server[1.31.0]" +``` + +#### Run pre-commit hooks manually: + +```bash +# Run on all files +pre-commit run --all-files + +# Run specific hook +pre-commit run ruff-check --all-files +``` + ## Contributing Check out [our contributing guide!](CONTRIBUTING.md) diff --git a/bin/install-ci-tools b/bin/install-ci-tools index f8b89bb3..0a3b3ebc 100755 --- a/bin/install-ci-tools +++ b/bin/install-ci-tools @@ -3,6 +3,7 @@ set -e KIND_VERSION="0.31.0" # https://github.com/kubernetes-sigs/kind/releases HELM_VERSION="3.20.0" # https://github.com/helm/helm/releases +UV_VERSION="0.10.7" # https://github.com/astral-sh/uv/releases # Determine the platform we are running on OS=$(uname | tr '[:upper:]' '[:lower:]') @@ -46,4 +47,12 @@ else chmod +x ./kubectl fi +echo "Installing uv version ${UV_VERSION}..." +if [[ -f /tmp/bin/uv ]]; then + echo "Already installed in /tmp/bin. Skipping!" +else + curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/${UV_VERSION}/uv-installer.sh | sh + mv "$HOME/.local/bin/uv" /tmp/bin/ +fi + cd "$CURRENT_DIR" diff --git a/templates/ingress.yaml b/templates/ingress.yaml index c8b3f1df..bb89fb12 100644 --- a/templates/ingress.yaml +++ b/templates/ingress.yaml @@ -31,6 +31,9 @@ metadata: {{- end }} {{- end }} spec: + {{- if .Values.ingress.ingressClassName }} + ingressClassName: {{ .Values.ingress.ingressClassName }} + {{- end }} tls: - secretName: {{ .Values.ingress.tlsSecretName | default "~" }} hosts: @@ -117,6 +120,9 @@ metadata: {{- end }} {{- end }} spec: + {{- if .Values.ingress.ingressClassName }} + ingressClassName: {{ .Values.ingress.ingressClassName }} + {{- end }} tls: - secretName: {{ .Values.ingress.tlsSecretName | default "~" }} hosts: @@ -189,6 +195,9 @@ metadata: {{- end }} {{- end }} spec: + {{- if .Values.ingress.ingressClassName }} + ingressClassName: {{ .Values.ingress.ingressClassName }} + {{- end }} tls: - secretName: {{ .Values.ingress.tlsSecretName | default "~" }} hosts: diff --git a/tests/chart/test_ingress.py b/tests/chart/test_ingress.py index b5bf517b..710917fa 100644 --- a/tests/chart/test_ingress.py +++ b/tests/chart/test_ingress.py @@ -131,3 +131,80 @@ def test_airflow_ingress_with_dag_server_ingress_annotation_and_tls_secret(self, assert ingressAnnotations == docs[1]["metadata"]["annotations"] assert docs[1]["spec"]["tls"][0]["secretName"] == tls_secret_name + + def test_airflow_ingress_class_name(self, kube_version): + """Test airflow ingress with ingressClassName.""" + docs = render_chart( + kube_version=kube_version, + show_only="templates/ingress.yaml", + values={ + "ingress": { + "enabled": True, + "baseDomain": "example.com", + "ingressClassName": "nginx", + } + }, + ) + assert len(docs) == 1 + doc = docs[0] + assert "Ingress" == doc["kind"] + assert "nginx" == doc["spec"]["ingressClassName"] + + def test_airflow_ingress_class_name_with_celery_executor(self, kube_version): + """Test airflow and flower ingress with ingressClassName and CeleryExecutor.""" + docs = render_chart( + kube_version=kube_version, + show_only="templates/ingress.yaml", + values={ + "airflow": {"executor": "CeleryExecutor"}, + "ingress": { + "enabled": True, + "baseDomain": "example.com", + "ingressClassName": "nginx-internal", + }, + }, + ) + assert len(docs) == 2 + + # Airflow ingress + assert "Ingress" == docs[0]["kind"] + assert "nginx-internal" == docs[0]["spec"]["ingressClassName"] + + # Flower ingress + assert "Ingress" == docs[1]["kind"] + assert "nginx-internal" == docs[1]["spec"]["ingressClassName"] + + def test_airflow_ingress_class_name_with_dag_server(self, kube_version): + """Test dag server ingress with ingressClassName.""" + docs = render_chart( + kube_version=kube_version, + show_only="templates/ingress.yaml", + values={ + "ingress": { + "baseDomain": "example.com", + "ingressClassName": "custom-ingress", + }, + "dagDeploy": {"enabled": True}, + }, + ) + + assert len(docs) == 1 + assert docs[0]["metadata"]["name"] == "release-name-dag-server-ingress" + assert "custom-ingress" == docs[0]["spec"]["ingressClassName"] + + def test_airflow_ingress_class_name_not_set(self, kube_version): + """Test airflow ingress without ingressClassName (default behavior).""" + docs = render_chart( + kube_version=kube_version, + show_only="templates/ingress.yaml", + values={ + "ingress": { + "enabled": True, + "baseDomain": "example.com", + } + }, + ) + assert len(docs) == 1 + doc = docs[0] + assert "Ingress" == doc["kind"] + assert "ingressClassName" not in doc["spec"] diff --git a/values.yaml b/values.yaml index 28600251..a5324ee8 100644 --- a/values.yaml +++ b/values.yaml @@ -590,6 +590,9 @@ ingress: # Enable ingress resource enabled: false + # Enables using a specific ingress class for the deployment. If not set, will default to the cluster default. + ingressClassName: ~ + # Enable for cert-manager or kube-lego acme: false