Skip to content

Commit 6720c57

Browse files
committed
Updating PR
1 parent fb99ec4 commit 6720c57

File tree

2 files changed

+56
-41
lines changed

2 files changed

+56
-41
lines changed

provider-kubeconfig.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ def _apply_provider_rbac(self, sa, namespace, kubeconfig):
203203
{"apiGroups": [""], "resources": ["resourcequotas"], "verbs": ["create", "delete", "deletecollection", "patch", "update"]},
204204
{"apiGroups": [""], "resources": ["persistentvolumes", "persistentvolumeclaims"], "verbs": ["get", "watch", "list", "create", "delete", "update", "patch"]},
205205
]
206-
# Match original: skip "*" resources (ruleGroup1, ruleGroup5, ruleGroup14)
206+
# Skip "*" wildcard resources — not meaningful in the perms inventory
207207
all_resources = [
208208
res for r in rule_list
209209
for res in r.get("resources", [])
@@ -366,16 +366,27 @@ def _extract_kubeconfig(self, sa, namespace, filename, serverip="", kubecfg="",
366366
else:
367367
time.sleep(2)
368368

369-
out, _ = run_command(" kubectl get secret " + sa + " -n " + namespace + " -o json " + kubecfg)
370-
json_out = json.loads(out or "{}")
371-
ca_cert = json_out.get("data", {}).get("ca.crt", "").strip()
369+
out, err = run_command(" kubectl get secret " + sa + " -n " + namespace + " -o json " + kubecfg)
370+
if not out:
371+
raise RuntimeError(
372+
f"Failed to fetch secret {sa!r} in ns {namespace!r}: {err}"
373+
)
374+
json_out = json.loads(out)
375+
ca_cert = json_out["data"]["ca.crt"].strip()
372376

373377
if serverip:
374378
server = serverip if "https" in serverip else "https://" + serverip
375379
else:
376-
out2, _ = run_command("kubectl -n default get endpoints kubernetes " + kubecfg + " | awk '{print $2}' | grep -v ENDPOINTS")
380+
out2, _ = run_command(
381+
"kubectl -n default get endpoints kubernetes " + kubecfg
382+
+ " | awk '{print $2}' | grep -v ENDPOINTS"
383+
)
377384
server = out2.strip() if out2 else ""
378385
server = "https://" + server if server else ""
386+
if not server or server.rstrip("/") == "https:":
387+
raise RuntimeError(
388+
"Could not determine API server endpoint; pass -s/--apiserverurl"
389+
)
379390

380391
self._create_kubecfg_file(sa, namespace, filename, token, ca_cert, server, kubecfg, cluster_name)
381392

tests/test_provider_kubeconfig.py

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import subprocess
88
import sys
99
import unittest
10+
import uuid
1011

1112

1213
def _run_command(cmd):
@@ -20,7 +21,7 @@ def _cluster_available(kubeconfig=""):
2021
path = os.path.expanduser(kubeconfig).strip() if kubeconfig else ""
2122
k_opt = " --kubeconfig=" + path if path else ""
2223
out, err = _run_command("kubectl get ns default" + k_opt)
23-
return out and "default" in out and "NotFound" not in (err or "")
24+
return out and "default" in out and "NotFound" not in err
2425

2526

2627
SCRIPT = "provider-kubeconfig.py"
@@ -50,11 +51,8 @@ def test_help_shows_all_actions_flags_and_namespace(self):
5051
)
5152
self.assertEqual(proc.returncode, 0, proc.stderr)
5253
out = proc.stdout or ""
53-
for elem in ["create", "delete", "update", "extract"]:
54-
self.assertIn(elem, out, f"Action {elem} should appear in help")
5554
for elem in HELP_ELEMENTS:
5655
self.assertIn(elem, out, f"Help should mention {elem}")
57-
self.assertIn("namespace", out.lower())
5856

5957
def test_update_without_permissionfile_exits_with_error(self):
6058
"""update action without -p exits with code 1."""
@@ -85,39 +83,45 @@ def setUp(self):
8583
if not self.has_cluster:
8684
self.skipTest("No cluster reachable (set KUBECONFIG)")
8785

88-
def _create_and_get_kubeconfig(self, root, ns, sa="kubeplus-saas-provider", extra_args=None, output_filename=None):
86+
def _create_and_get_kubeconfig(self, ns, sa="kubeplus-saas-provider", extra_args=None, output_filename=None):
8987
"""Run create, return (kubeconfig_dict, proc). Caller must delete to cleanup."""
9088
create_args = ["create", ns]
9189
if sa != "kubeplus-saas-provider":
9290
create_args += ["-c", sa]
9391
if extra_args:
9492
create_args += extra_args
9593
proc = subprocess.run(
96-
[sys.executable, os.path.join(root, SCRIPT)] + create_args + self.kubeconfig_arg,
97-
cwd=root, capture_output=True, text=True, timeout=120,
94+
[sys.executable, os.path.join(ROOT, SCRIPT)] + create_args + self.kubeconfig_arg,
95+
cwd=ROOT, capture_output=True, text=True, timeout=120,
9896
)
9997
if proc.returncode != 0:
10098
return None, proc
10199
filename = output_filename or (sa + ".json")
102100
if not filename.endswith(".json"):
103101
filename += ".json"
104-
kubeconfig_path = os.path.join(root, filename)
102+
kubeconfig_path = os.path.join(ROOT, filename)
105103
if not os.path.exists(kubeconfig_path):
106-
return None, proc
104+
raise AssertionError(
105+
f"Script exited 0 but kubeconfig not written: {kubeconfig_path}"
106+
)
107107
with open(kubeconfig_path, "r", encoding="utf-8") as fp:
108108
cfg = json.load(fp)
109109
return cfg, proc
110110

111-
def _delete_for_cleanup(self, root, ns, sa="kubeplus-saas-provider", filename=None):
112-
"""Delete k8s resources and local files. Pass filename when -f was used (e.g. custom-provider-kubeconfig)."""
111+
def _delete_for_cleanup(self, ns, sa="kubeplus-saas-provider", filename=None):
112+
"""Delete k8s resources, local files, and test namespace."""
113113
delete_args = ["delete", ns]
114114
if sa != "kubeplus-saas-provider":
115115
delete_args += ["-c", sa]
116116
if filename:
117117
delete_args += ["-f", filename]
118118
subprocess.run(
119-
[sys.executable, os.path.join(root, SCRIPT)] + delete_args + self.kubeconfig_arg,
120-
cwd=root, capture_output=True, timeout=60,
119+
[sys.executable, os.path.join(ROOT, SCRIPT)] + delete_args + self.kubeconfig_arg,
120+
cwd=ROOT, capture_output=True, timeout=60,
121+
)
122+
_run_command(
123+
"kubectl delete namespace " + ns
124+
+ " --ignore-not-found --wait=false" + self.kubeconfig_flag
121125
)
122126

123127
def _assert_kubeconfig_valid(
@@ -174,13 +178,13 @@ def _assert_kubeconfig_valid(
174178

175179
def _sa_exists(self, sa, ns):
176180
out, err = _run_command("kubectl get sa " + sa + " -n " + ns + self.kubeconfig_flag)
177-
return out and sa in out and "NotFound" not in (err or "")
181+
return out and sa in out and "NotFound" not in err
178182

179183
def test_provider_kubeconfig_all_fields_nonempty(self):
180184
"""Provider kubeconfig: every field that should exist is non-empty."""
181-
ns = "kubeplus-test-prov-" + str(os.getpid())
185+
ns = "kubeplus-test-prov-" + uuid.uuid4().hex[:8]
182186
try:
183-
cfg, proc = self._create_and_get_kubeconfig(ROOT, ns)
187+
cfg, proc = self._create_and_get_kubeconfig(ns)
184188
self.assertEqual(proc.returncode, 0, proc.stderr)
185189
self._assert_kubeconfig_valid(
186190
cfg,
@@ -189,14 +193,14 @@ def test_provider_kubeconfig_all_fields_nonempty(self):
189193
)
190194
self.assertTrue(self._sa_exists("kubeplus-saas-provider", ns))
191195
finally:
192-
self._delete_for_cleanup(ROOT, ns)
196+
self._delete_for_cleanup(ns)
193197

194198
def test_consumer_kubeconfig_all_fields_nonempty(self):
195199
"""Consumer kubeconfig: every field non-empty, user name matches SA, namespace in context."""
196-
ns = "kubeplus-test-cons-" + str(os.getpid())
200+
ns = "kubeplus-test-cons-" + uuid.uuid4().hex[:8]
197201
consumer_sa = "test-consumer-sa"
198202
try:
199-
cfg, proc = self._create_and_get_kubeconfig(ROOT, ns, sa=consumer_sa)
203+
cfg, proc = self._create_and_get_kubeconfig(ns, sa=consumer_sa)
200204
self.assertEqual(proc.returncode, 0, proc.stderr)
201205
self._assert_kubeconfig_valid(
202206
cfg,
@@ -205,29 +209,29 @@ def test_consumer_kubeconfig_all_fields_nonempty(self):
205209
)
206210
self.assertTrue(self._sa_exists(consumer_sa, ns))
207211
finally:
208-
self._delete_for_cleanup(ROOT, ns, sa=consumer_sa)
212+
self._delete_for_cleanup(ns, sa=consumer_sa)
209213

210214
def test_flag_s_apiserverurl_reflected_in_kubeconfig(self):
211215
"""-s/--apiserverurl sets cluster server in kubeconfig."""
212-
ns = "kubeplus-test-s-" + str(os.getpid())
216+
ns = "kubeplus-test-s-" + uuid.uuid4().hex[:8]
213217
test_server = "https://api.example.com:6443"
214218
try:
215219
cfg, proc = self._create_and_get_kubeconfig(
216-
ROOT, ns,
220+
ns,
217221
extra_args=["-s", test_server],
218222
)
219223
self.assertEqual(proc.returncode, 0, proc.stderr)
220224
self._assert_kubeconfig_valid(cfg, expected_server=test_server, expected_namespace=ns)
221225
finally:
222-
self._delete_for_cleanup(ROOT, ns)
226+
self._delete_for_cleanup(ns)
223227

224228
def test_flag_x_clustername_reflected_in_kubeconfig(self):
225229
"""-x/--clustername sets context name and cluster name in kubeconfig."""
226-
ns = "kubeplus-test-x-" + str(os.getpid())
230+
ns = "kubeplus-test-x-" + uuid.uuid4().hex[:8]
227231
test_cluster = "my-test-cluster"
228232
try:
229233
cfg, proc = self._create_and_get_kubeconfig(
230-
ROOT, ns,
234+
ns,
231235
extra_args=["-x", test_cluster],
232236
)
233237
self.assertEqual(proc.returncode, 0, proc.stderr)
@@ -237,32 +241,32 @@ def test_flag_x_clustername_reflected_in_kubeconfig(self):
237241
expected_namespace=ns,
238242
)
239243
finally:
240-
self._delete_for_cleanup(ROOT, ns)
244+
self._delete_for_cleanup(ns)
241245

242246
def test_flag_f_filename_uses_custom_output_file(self):
243247
"""-f/--filename writes kubeconfig to specified file."""
244-
ns = "kubeplus-test-f-" + str(os.getpid())
248+
ns = "kubeplus-test-f-" + uuid.uuid4().hex[:8]
245249
custom_name = "custom-provider-kubeconfig"
246250
try:
247251
cfg, proc = self._create_and_get_kubeconfig(
248-
ROOT, ns,
252+
ns,
249253
extra_args=["-f", custom_name],
250254
output_filename=custom_name,
251255
)
252256
self.assertEqual(proc.returncode, 0, proc.stderr)
253257
self._assert_kubeconfig_valid(cfg, expected_namespace=ns)
254258
self.assertTrue(os.path.exists(os.path.join(ROOT, custom_name + ".json")))
255259
finally:
256-
self._delete_for_cleanup(ROOT, ns, filename=custom_name)
260+
self._delete_for_cleanup(ns, filename=custom_name)
257261

258262
def test_flags_s_and_x_combined(self):
259263
"""-s and -x together: both server and cluster name in kubeconfig."""
260-
ns = "kubeplus-test-sx-" + str(os.getpid())
264+
ns = "kubeplus-test-sx-" + uuid.uuid4().hex[:8]
261265
test_server = "https://api.example.com:6443"
262266
test_cluster = "my-test-cluster"
263267
try:
264268
cfg, proc = self._create_and_get_kubeconfig(
265-
ROOT, ns,
269+
ns,
266270
extra_args=["-s", test_server, "-x", test_cluster],
267271
)
268272
self.assertEqual(proc.returncode, 0, proc.stderr)
@@ -273,19 +277,19 @@ def test_flags_s_and_x_combined(self):
273277
expected_namespace=ns,
274278
)
275279
finally:
276-
self._delete_for_cleanup(ROOT, ns)
280+
self._delete_for_cleanup(ns)
277281

278282
def test_consumer_cannot_create_pod_in_other_namespace(self):
279283
"""
280284
Consumer kubeconfig: verify create/delete in other namespaces is forbidden.
281285
Consumer RBAC should restrict operations; creating a pod in another ns should fail.
282286
"""
283-
ns = "kubeplus-test-restrict-" + str(os.getpid())
284-
other_ns = "kubeplus-test-other-" + str(os.getpid())
287+
ns = "kubeplus-test-restrict-" + uuid.uuid4().hex[:8]
288+
other_ns = "kubeplus-test-other-" + uuid.uuid4().hex[:8]
285289
consumer_sa = "test-consumer-restrict"
286290
kubeconfig_path = os.path.join(ROOT, consumer_sa + ".json")
287291
try:
288-
cfg, proc = self._create_and_get_kubeconfig(ROOT, ns, sa=consumer_sa)
292+
cfg, proc = self._create_and_get_kubeconfig(ns, sa=consumer_sa)
289293
self.assertEqual(proc.returncode, 0, proc.stderr)
290294
self._assert_kubeconfig_valid(cfg, expected_namespace=ns, expected_user_name=consumer_sa)
291295

@@ -305,7 +309,7 @@ def test_consumer_cannot_create_pod_in_other_namespace(self):
305309
)
306310
finally:
307311
_run_command("kubectl delete namespace " + other_ns + self.kubeconfig_flag + " 2>/dev/null")
308-
self._delete_for_cleanup(ROOT, ns, sa=consumer_sa)
312+
self._delete_for_cleanup(ns, sa=consumer_sa)
309313

310314

311315
if __name__ == "__main__":

0 commit comments

Comments
 (0)