Skip to content

Commit 71d39c3

Browse files
committed
chore: add test to reproduce Airflow / OPA interop issue
1 parent 81de81f commit 71d39c3

13 files changed

Lines changed: 475 additions & 1 deletion
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{% if test_scenario['values']['openshift'] == 'true' %}
2+
# see https://github.com/stackabletech/issues/issues/566
3+
---
4+
apiVersion: kuttl.dev/v1beta1
5+
kind: TestStep
6+
commands:
7+
- script: kubectl patch namespace $NAMESPACE -p '{"metadata":{"labels":{"pod-security.kubernetes.io/enforce":"privileged"}}}'
8+
timeout: 120
9+
{% endif %}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
apiVersion: kuttl.dev/v1beta1
3+
kind: TestAssert
4+
metadata:
5+
name: test-airflow-postgresql
6+
timeout: 480
7+
---
8+
apiVersion: apps/v1
9+
kind: StatefulSet
10+
metadata:
11+
name: airflow-postgresql
12+
status:
13+
readyReplicas: 1
14+
replicas: 1
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
apiVersion: kuttl.dev/v1beta1
3+
kind: TestStep
4+
commands:
5+
- script: >-
6+
helm install airflow-postgresql
7+
--namespace $NAMESPACE
8+
--version 12.5.6
9+
--values 11_helm-bitnami-postgresql-values.yaml
10+
--repo https://charts.bitnami.com/bitnami postgresql
11+
--wait
12+
timeout: 600
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
global:
3+
security:
4+
allowInsecureImages: true
5+
6+
image:
7+
repository: bitnamilegacy/postgresql
8+
9+
volumePermissions:
10+
enabled: false
11+
image:
12+
repository: bitnamilegacy/os-shell
13+
securityContext:
14+
runAsUser: auto
15+
16+
metrics:
17+
image:
18+
repository: bitnamilegacy/postgres-exporter
19+
20+
primary:
21+
podSecurityContext:
22+
{% if test_scenario['values']['openshift'] == 'true' %}
23+
enabled: false
24+
{% else %}
25+
enabled: true
26+
{% endif %}
27+
containerSecurityContext:
28+
enabled: false
29+
resources:
30+
requests:
31+
memory: "128Mi"
32+
cpu: "512m"
33+
limits:
34+
memory: "128Mi"
35+
cpu: "1"
36+
37+
shmVolume:
38+
chmod:
39+
enabled: false
40+
41+
auth:
42+
username: airflow
43+
password: airflow
44+
database: airflow
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
apiVersion: kuttl.dev/v1beta1
3+
kind: TestAssert
4+
timeout: 300
5+
commands:
6+
- script: kubectl -n $NAMESPACE wait --for=condition=available --timeout=10m opacluster/test-opa
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
apiVersion: kuttl.dev/v1beta1
3+
kind: TestStep
4+
metadata:
5+
name: install-opa
6+
---
7+
apiVersion: opa.stackable.tech/v1alpha1
8+
kind: OpaCluster
9+
metadata:
10+
name: test-opa
11+
spec:
12+
image:
13+
{% if test_scenario['values']['opa-latest'].find(",") > 0 %}
14+
custom: "{{ test_scenario['values']['opa-latest'].split(',')[1] }}"
15+
productVersion: "{{ test_scenario['values']['opa-latest'].split(',')[0] }}"
16+
{% else %}
17+
productVersion: "{{ test_scenario['values']['opa-latest'] }}"
18+
{% endif %}
19+
pullPolicy: IfNotPresent
20+
clusterConfig:
21+
{% if lookup('env', 'VECTOR_AGGREGATOR') %}
22+
vectorAggregatorConfigMapName: vector-aggregator-discovery
23+
{% endif %}
24+
servers:
25+
config:
26+
logging:
27+
enableVectorAgent: {{ lookup('env', 'VECTOR_AGGREGATOR') | length > 0 }}
28+
containers:
29+
opa:
30+
loggers:
31+
decision:
32+
level: INFO
33+
roleGroups:
34+
default: {}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
apiVersion: kuttl.dev/v1beta1
3+
kind: TestAssert
4+
metadata:
5+
name: install-airflow
6+
timeout: 1200
7+
commands:
8+
- script: >
9+
kubectl --namespace $NAMESPACE
10+
wait --for=condition=available=true
11+
airflowclusters.airflow.stackable.tech/airflow
12+
--timeout 301s
13+
---
14+
apiVersion: apps/v1
15+
kind: StatefulSet
16+
metadata:
17+
name: airflow-webserver-default
18+
status:
19+
readyReplicas: 1
20+
replicas: 1
21+
---
22+
apiVersion: apps/v1
23+
kind: StatefulSet
24+
metadata:
25+
name: airflow-scheduler-default
26+
status:
27+
readyReplicas: 1
28+
replicas: 1
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
apiVersion: kuttl.dev/v1beta1
3+
kind: TestStep
4+
metadata:
5+
name: install-airflow
6+
---
7+
apiVersion: v1
8+
kind: Secret
9+
metadata:
10+
name: airflow-admin-credentials
11+
type: Opaque
12+
stringData:
13+
adminUser.username: airflow
14+
adminUser.firstname: Airflow
15+
adminUser.lastname: Admin
16+
adminUser.email: airflow@airflow.com
17+
adminUser.password: airflow
18+
---
19+
apiVersion: v1
20+
kind: Secret
21+
metadata:
22+
name: airflow-postgresql-credentials
23+
stringData:
24+
username: airflow
25+
password: airflow
26+
---
27+
apiVersion: airflow.stackable.tech/v1alpha1
28+
kind: AirflowCluster
29+
metadata:
30+
name: airflow
31+
spec:
32+
image:
33+
{% if test_scenario['values']['airflow-latest'].find(",") > 0 %}
34+
custom: "{{ test_scenario['values']['airflow-latest'].split(',')[1] }}"
35+
productVersion: "{{ test_scenario['values']['airflow-latest'].split(',')[0] }}"
36+
{% else %}
37+
productVersion: "{{ test_scenario['values']['airflow-latest'] }}"
38+
{% endif %}
39+
pullPolicy: IfNotPresent
40+
clusterConfig:
41+
authorization:
42+
opa:
43+
configMapName: test-opa
44+
package: airflow
45+
credentialsSecretName: airflow-admin-credentials
46+
metadataDatabase:
47+
postgresql:
48+
host: airflow-postgresql
49+
database: airflow
50+
credentialsSecretName: airflow-postgresql-credentials
51+
loadExamples: true
52+
webservers:
53+
roleConfig:
54+
listenerClass: external-unstable
55+
envOverrides:
56+
AIRFLOW__CORE__AUTH_OPA_CACHE_MAXSIZE: "0"
57+
roleGroups:
58+
default:
59+
replicas: 1
60+
kubernetesExecutors: {}
61+
schedulers:
62+
roleGroups:
63+
default:
64+
replicas: 1
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
apiVersion: v1
3+
kind: ConfigMap
4+
metadata:
5+
name: airflow-rules
6+
labels:
7+
opa.stackable.tech/bundle: "true"
8+
data:
9+
airflow.rego: |
10+
package airflow
11+
12+
# Default deny everything. Only the rules below should permit anything.
13+
default is_authorized_configuration := false
14+
default is_authorized_connection := false
15+
default is_authorized_dag := false
16+
default is_authorized_pool := false
17+
default is_authorized_variable := false
18+
default is_authorized_view := false
19+
default is_authorized_custom_view := false
20+
default is_authorized_backfill := false
21+
default is_authorized_asset := false
22+
default is_authorized_asset_alias := false
23+
24+
# Allow-all for the "airflow" admin so the operator can bootstrap the
25+
# cluster (creating the admin user, syncing example DAGs, etc).
26+
is_authorized_configuration if input.user.name == "airflow"
27+
is_authorized_connection if input.user.name == "airflow"
28+
is_authorized_dag if input.user.name == "airflow"
29+
is_authorized_pool if input.user.name == "airflow"
30+
is_authorized_variable if input.user.name == "airflow"
31+
is_authorized_view if input.user.name == "airflow"
32+
is_authorized_custom_view if input.user.name == "airflow"
33+
is_authorized_backfill if input.user.name == "airflow"
34+
is_authorized_asset if input.user.name == "airflow"
35+
is_authorized_asset_alias if input.user.name == "airflow"
36+
37+
# jane.doe is OPA-allow-all - same shape as the airflow admin block above.
38+
# Intentionally broad: the goal of this test is to isolate the FAB
39+
# filter/listing layer. Any 403 we see for jane.doe must therefore come
40+
# from FAB (the layer with the bug), not from OPA denying some auxiliary
41+
# check (is_authorized_view, is_authorized_configuration, ...) that the
42+
# listing endpoint also performs.
43+
#
44+
# With this rule + Admin role: both layers say yes -> test passes.
45+
# With this rule + Public role: OPA says yes everywhere, FAB's
46+
# unoverridden get_authorized_dag_ids says no -> 403 -> test fails (bug).
47+
is_authorized_configuration if input.user.name == "jane.doe"
48+
is_authorized_connection if input.user.name == "jane.doe"
49+
is_authorized_dag if input.user.name == "jane.doe"
50+
is_authorized_pool if input.user.name == "jane.doe"
51+
is_authorized_variable if input.user.name == "jane.doe"
52+
is_authorized_view if input.user.name == "jane.doe"
53+
is_authorized_custom_view if input.user.name == "jane.doe"
54+
is_authorized_backfill if input.user.name == "jane.doe"
55+
is_authorized_asset if input.user.name == "jane.doe"
56+
is_authorized_asset_alias if input.user.name == "jane.doe"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
apiVersion: kuttl.dev/v1beta1
3+
kind: TestStep
4+
metadata:
5+
name: create-users
6+
timeout: 300
7+
commands:
8+
- script: |
9+
kubectl exec -n $NAMESPACE airflow-webserver-default-0 -- airflow users create \
10+
--username "jane.doe" \
11+
--firstname "Jane" \
12+
--lastname "Doe" \
13+
--email "jane.doe@stackable.tech" \
14+
--password "T8mn72D9" \
15+
--role "Public"
16+
kubectl exec -n $NAMESPACE airflow-webserver-default-0 -- airflow users create \
17+
--username "john.doe" \
18+
--firstname "John" \
19+
--lastname "Doe" \
20+
--email "john.doe@stackable.tech" \
21+
--password "T8mn72D9" \
22+
--role "Admin"

0 commit comments

Comments
 (0)