77import subprocess
88import sys
99import unittest
10+ import uuid
1011
1112
1213def _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
2627SCRIPT = "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
311315if __name__ == "__main__" :
0 commit comments