Skip to content

Commit 8c94790

Browse files
authored
Merge pull request #783 from bitcoin-dev-project/kconfig-players-only
Generate kubeconfig for human players only
2 parents 49adbed + e596d6c commit 8c94790

4 files changed

Lines changed: 55 additions & 20 deletions

File tree

resources/charts/namespaces/templates/serviceaccount.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ metadata:
88
annotations:
99
helm.sh/hook: post-install,post-upgrade
1010
helm.sh/hook-weight: "-5"
11+
labels:
12+
mission: user
1113
{{- end }}

src/warnet/admin.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
K8sError,
1212
get_cluster_of_current_context,
1313
get_namespaces_by_type,
14-
get_service_accounts_in_namespace,
14+
get_token_for_service_acount,
15+
get_warnet_user_service_accounts_in_namespace,
1516
open_kubeconfig,
1617
)
1718
from .namespaces import copy_namespaces_defaults, namespaces
1819
from .network import copy_network_defaults
19-
from .process import run_command
2020

2121

2222
@click.group(name="admin", hidden=True)
@@ -84,21 +84,21 @@ def create_kubeconfigs(kubeconfig_dir, token_duration):
8484
for v1namespace in warnet_namespaces:
8585
namespace = v1namespace.metadata.name
8686
click.echo(f"Processing namespace: {namespace}")
87-
service_accounts = get_service_accounts_in_namespace(namespace)
87+
service_accounts = get_warnet_user_service_accounts_in_namespace(namespace)
8888

8989
for sa in service_accounts:
90+
name = sa.metadata.name
9091
# Create a token for the ServiceAccount with specified duration
91-
command = f"kubectl create token {sa} -n {namespace} --duration={token_duration}s"
9292
try:
93-
token = run_command(command)
93+
token = get_token_for_service_acount(sa, token_duration)
9494
except Exception as e:
9595
click.echo(
96-
f"Failed to create token for ServiceAccount {sa} in namespace {namespace}. Error: {str(e)}. Skipping..."
96+
f"Failed to create token for ServiceAccount {name} in namespace {namespace}. Error: {str(e)}. Skipping..."
9797
)
9898
continue
9999

100100
# Create a kubeconfig file for the user
101-
kubeconfig_file = os.path.join(kubeconfig_dir, f"{sa}-{namespace}-kubeconfig")
101+
kubeconfig_file = os.path.join(kubeconfig_dir, f"{name}-{namespace}-kubeconfig")
102102

103103
# TODO: move yaml out of python code to resources/manifests/
104104
#
@@ -109,21 +109,25 @@ def create_kubeconfigs(kubeconfig_dir, token_duration):
109109
"apiVersion": "v1",
110110
"kind": "Config",
111111
"clusters": [cluster],
112-
"users": [{"name": sa, "user": {"token": token}}],
112+
"users": [{"name": name, "user": {"token": token}}],
113113
"contexts": [
114114
{
115-
"name": f"{sa}-{namespace}",
116-
"context": {"cluster": cluster["name"], "namespace": namespace, "user": sa},
115+
"name": f"{name}-{namespace}",
116+
"context": {
117+
"cluster": cluster["name"],
118+
"namespace": namespace,
119+
"user": name,
120+
},
117121
}
118122
],
119-
"current-context": f"{sa}-{namespace}",
123+
"current-context": f"{name}-{namespace}",
120124
}
121125

122126
# Write to a YAML file
123127
with open(kubeconfig_file, "w") as f:
124128
yaml.dump(kubeconfig_dict, f, default_flow_style=False)
125129

126-
click.echo(f" Created kubeconfig file for {sa}: {kubeconfig_file}")
130+
click.echo(f" Created kubeconfig file for {name}: {kubeconfig_file}")
127131

128132
click.echo("---")
129133
click.echo(

src/warnet/k8s.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
import tarfile
55
import tempfile
66
from pathlib import Path
7-
from time import time, sleep
7+
from time import sleep, time
88
from typing import Optional
99

1010
import yaml
1111
from kubernetes import client, config, watch
1212
from kubernetes.client import CoreV1Api
13-
from kubernetes.client.models import V1Namespace, V1Pod, V1PodList
13+
from kubernetes.client.models import V1Namespace, V1Pod, V1PodList, V1TokenRequestSpec
1414
from kubernetes.client.rest import ApiException
1515
from kubernetes.dynamic import DynamicClient
1616
from kubernetes.stream import stream
@@ -516,14 +516,32 @@ def get_namespaces_by_type(namespace_type: str) -> list[V1Namespace]:
516516
return [ns for ns in namespaces if ns.metadata.name.startswith(namespace_type)]
517517

518518

519-
def get_service_accounts_in_namespace(namespace):
519+
def get_warnet_user_service_accounts_in_namespace(namespace):
520520
"""
521-
Get all service accounts in a namespace. Returns an empty list if no service accounts are found in the specified namespace.
521+
Get all service accounts in a namespace that were created for human users
522+
(not scenario commanders or other pods)
523+
Returns an empty list if no applicable service accounts are found in the specified namespace.
522524
"""
523-
command = f"kubectl get serviceaccounts -n {namespace} -o jsonpath={{.items[*].metadata.name}}"
524-
# skip the default service account created by k8s
525-
service_accounts = run_command(command).split()
526-
return [sa for sa in service_accounts if sa != "default"]
525+
sclient = get_static_client()
526+
sas = sclient.list_namespaced_service_account(namespace)
527+
return [
528+
sa
529+
for sa in sas.items
530+
if sa.metadata.labels
531+
and "mission" in sa.metadata.labels
532+
and sa.metadata.labels["mission"] == "user"
533+
]
534+
535+
536+
def get_token_for_service_acount(sa, duration):
537+
sclient = get_static_client()
538+
spec = V1TokenRequestSpec(
539+
audiences=["https://kubernetes.default.svc"], expiration_seconds=duration
540+
)
541+
resp = sclient.create_namespaced_service_account_token(
542+
name=sa.metadata.name, namespace=sa.metadata.namespace, body=spec
543+
)
544+
return resp.status.token
527545

528546

529547
def can_delete_pods(namespace: Optional[str] = None) -> bool:

test/wargames_test.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,17 @@ def check_scenario_permissions(self):
8787
# Sanity check
8888
assert self.warnet("bitcoin rpc miner getblockcount") == "6"
8989

90+
self.log.info("Re-generate kubeconfigs after a scenario has been run")
91+
self.log.info(self.warnet("admin create-kubeconfigs"))
92+
kubeconfig_dir = Path("kubeconfigs/")
93+
count = 0
94+
for p in kubeconfig_dir.iterdir():
95+
if p.is_file():
96+
print(p)
97+
assert "warnet-user" in str(p)
98+
count += 1
99+
assert count <= 1
100+
90101

91102
if __name__ == "__main__":
92103
test = WargamesTest()

0 commit comments

Comments
 (0)