Skip to content

Commit

Permalink
[DPE-3398] Fix Spark service account permission (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
deusebio authored Feb 1, 2024
1 parent cafbe78 commit e44d9b6
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 36 deletions.
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ $(k8s_tag):
microk8s: $(k8s_tag)

integration-tests: setup microk8s
echo "Integration tests"
echo "Integration tests (Python)"
sg microk8s "${PYTHON} tox -e integration"
echo "Integration tests (Bash)"
sg microk8s "./tests/integration/tests_in_pods.sh"

clean:
@echo "==Cleaning environment=="
Expand Down
12 changes: 1 addition & 11 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 1 addition & 11 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,15 @@ markupsafe==2.1.3 ; python_full_version > "3.8.0" and python_version < "4.0" \
--hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \
--hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \
--hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \
--hash=sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c \
--hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \
--hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \
--hash=sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb \
--hash=sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939 \
--hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \
--hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \
--hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \
--hash=sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9 \
--hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \
--hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \
--hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \
--hash=sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd \
--hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \
--hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \
--hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \
Expand All @@ -57,20 +53,16 @@ markupsafe==2.1.3 ; python_full_version > "3.8.0" and python_version < "4.0" \
--hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \
--hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \
--hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \
--hash=sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007 \
--hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \
--hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \
--hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \
--hash=sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0 \
--hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \
--hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \
--hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \
--hash=sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1 \
--hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \
--hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \
--hash=sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c \
--hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \
--hash=sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823 \
--hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \
--hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \
--hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \
Expand All @@ -89,9 +81,7 @@ markupsafe==2.1.3 ; python_full_version > "3.8.0" and python_version < "4.0" \
--hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \
--hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \
--hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \
--hash=sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc \
--hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \
--hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11
--hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2
pyyaml==6.0 ; python_full_version > "3.8.0" and python_version < "4.0" \
--hash=sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf \
--hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \
Expand Down
2 changes: 2 additions & 0 deletions spark8t/resources/templates/role_yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ rules:
- pods
- configmaps
- services
- serviceaccounts
- secrets
verbs:
- create
- get
Expand Down
31 changes: 22 additions & 9 deletions spark8t/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,11 +336,14 @@ def get_service_accounts(

if not namespace:
# means all namespaces
iterator = self.client.list(
res=Namespace,
)
for ns in iterator:
all_namespaces.append(ns.metadata.name)
try:
iterator = self.client.list(
res=Namespace,
)
for ns in iterator:
all_namespaces.append(ns.metadata.name)
except Exception:
all_namespaces.append(self.namespace)

else:
all_namespaces = [
Expand Down Expand Up @@ -731,9 +734,13 @@ def get_service_accounts(
if labels is not None and len(labels) > 0:
cmd += " ".join([f" -l {label}" for label in labels])

namespace = " -A" if namespace is None else f" -n {namespace}"

all_service_accounts_raw = self.exec(cmd + namespace, namespace=None)
if namespace:
all_service_accounts_raw = self.exec(cmd, namespace=namespace)
else:
try:
all_service_accounts_raw = self.exec(f"{cmd} -A", namespace=None)
except subprocess.CalledProcessError:
all_service_accounts_raw = self.exec(cmd, namespace=self.namespace)

if isinstance(all_service_accounts_raw, str):
raise ValueError("Malformed output")
Expand Down Expand Up @@ -1125,7 +1132,13 @@ def create(self, service_account: ServiceAccount) -> str:
rolename,
namespace=service_account.namespace,
**{
"resource": ["pods", "configmaps", "services"],
"resource": [
"pods",
"configmaps",
"services",
"serviceaccounts",
"secrets",
],
"verb": ["create", "get", "list", "watch", "delete"],
},
)
Expand Down
2 changes: 0 additions & 2 deletions spark8t/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,6 @@ def execute_command_output(cmd: str) -> str:
cmd, shell=True, stderr=subprocess.STDOUT
).decode("utf-8")
except subprocess.CalledProcessError as e:
print(e.stderr)
print(e.stdout)
raise e

return output
Expand Down
145 changes: 145 additions & 0 deletions tests/integration/tests_in_pods.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#!/bin/bash

# This file is used to test the spark8t library within a K8s cluster.
# Only the functionalities related to the handling of service accounts can
# be tested, since spark-submit, pyspark and spark-shell would require
# the spark binaries.

setup_env() {
NAMESPACES=$*

for NAMESPACE in $NAMESPACES; do
kubectl create namespace ${NAMESPACE}
done

poetry install
}

create_service_account(){

USERNAME=$1
NAMESPACE=$2

poetry run python -m spark8t.cli.service_account_registry create \
--username $USERNAME --namespace $NAMESPACE
}

setup_test_pod() {
NAMESPACE=$1

kubectl -n $NAMESPACE apply -f ./tests/resources/pod.yaml

SLEEP_TIME=1
for i in {1..5}
do
pod_status=$(kubectl -n $NAMESPACE get pod testpod| awk '{ print $3 }' | tail -n 1)
echo $pod_status
if [[ "${pod_status}" == "Running" ]]
then
echo "testpod is Running now!"
break
elif [[ "${i}" -le "5" ]]
then
echo "Waiting for the pod to come online..."
sleep $SLEEP_TIME
else
echo "testpod did not come up. Test Failed!"
exit 3
fi
SLEEP_TIME=$(expr $SLEEP_TIME \* 2);
done

poetry build

# Install spark8t
TARBALL=$(find dist -name "*.tar.gz" | tail -n1)
kubectl -n $NAMESPACE cp "${TARBALL}" testpod:/home/.
kubectl -n $NAMESPACE exec testpod -- /bin/bash -c "pip install /home/${TARBALL##*/}"

# Install kubectl
VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt)
kubectl -n $NAMESPACE exec testpod -- curl -LO "https://dl.k8s.io/release/${VERSION}/bin/linux/amd64/kubectl"
kubectl -n $NAMESPACE exec testpod -- mv kubectl /usr/local/bin
kubectl -n $NAMESPACE exec testpod -- chmod +x /usr/local/bin/kubectl
}

check_service_accounts_in_pod() {

CMD=$1
CHECK=$2
EXPECTED_RESULT=$3

NAMESPACE=$(kubectl get pod --field-selector metadata.name=testpod -A | cut -d " " -f1 | tail -n1)

echo -e "$(kubectl -n $NAMESPACE exec testpod -- env CMD="$CMD" /bin/bash -c 'python -m spark8t.cli.service_account_registry $CMD')" > spark8t.out

out=$(/bin/bash -c "cat spark8t.out | $CHECK")

if [ "${out}" != "${EXPECTED_RESULT}" ]; then
echo "ERROR: Wrong value of fetched Spark service accounts"
exit 1
fi
}

check_service_accounts_admin() {

CMD=$1
CHECK=$2
EXPECTED_RESULT=$3

poetry run python -m spark8t.cli.service_account_registry $CMD > spark8t.out

out=$(cat spark8t.out | $CHECK)

if [ "${out}" != "${EXPECTED_RESULT}" ]; then
echo "ERROR: Wrong value of fetched Spark service accounts"
exit 1
fi
}

cleanup_user() {
EXIT_CODE=$1
shift
NAMESPACES=$*

for NAMESPACE in $NAMESPACES; do
kubectl delete namespace ${NAMESPACE}
done

if [ "${EXIT_CODE}" -ne "0" ]; then
exit 1
fi
}

cleanup_success() {
echo "cleanup_success()......"
cleanup_user 0 $*
}

cleanup_failure() {
echo "cleanup_failure()......"
cleanup_user 1 $*
}

# Test listing spark accounts in a pod with restricted visibility
( \
setup_env test test-2 && \
create_service_account spark test && \
create_service_account spark test-2 && \
check_service_accounts_admin "list --backend lightkube" "wc -l" "2" && \
check_service_accounts_admin "list --backend kubectl" "wc -l" "2" && \
setup_test_pod test && \
check_service_accounts_in_pod "list --backend lightkube" "wc -l" "1" && \
check_service_accounts_in_pod "list --backend kubectl" "wc -l" "1" && \
cleanup_success test test-2 \
) || cleanup_failure test test-2

# Test get-config for spark accounts in a pod with restricted visibility
( \
setup_env test && \
create_service_account spark test && \
setup_test_pod test && \
check_service_accounts_in_pod "get-config --username spark --namespace test --backend lightkube" "grep spark.kubernetes | wc -l" "2" && \
check_service_accounts_in_pod "get-config --username spark --namespace test --backend kubectl" "grep spark.kubernetes | wc -l" "2" && \
cleanup_success test \
) || cleanup_failure test
16 changes: 16 additions & 0 deletions tests/resources/pod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: v1
kind: Pod
metadata:
name: testpod
labels:
app: ubuntu
spec:
serviceAccount: spark
containers:
- image: docker.io/python:3.10
command:
- "sleep"
- "604800"
imagePullPolicy: Always
name: ubuntu
restartPolicy: Always
10 changes: 8 additions & 2 deletions tests/unittest/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -1097,7 +1097,7 @@ def test_kube_interface_get_service_accounts(mocker, tmp_path):
with open(kube_config_file, "w") as fid:
yaml.dump(kubeconfig_yaml, fid, sort_keys=False)

cmd_get_sa = f"kubectl --kubeconfig {kube_config_file} --context {context} get serviceaccount -l {label1} -l {label2} -n {namespace} -o yaml"
cmd_get_sa = f"kubectl --kubeconfig {kube_config_file} --namespace {namespace} --context {context} get serviceaccount -l {label1} -l {label2} -o yaml"
output_get_sa_yaml = {
"apiVersion": "v1",
"items": [
Expand Down Expand Up @@ -1410,7 +1410,13 @@ def test_k8s_registry_create(mocker):
f"{name3}-role",
namespace=namespace3,
**{
"resource": ["pods", "configmaps", "services"],
"resource": [
"pods",
"configmaps",
"services",
"serviceaccounts",
"secrets",
],
"verb": ["create", "get", "list", "watch", "delete"],
},
)
Expand Down

0 comments on commit e44d9b6

Please sign in to comment.