unitlab 2.3.17__py3-none-any.whl → 2.3.20__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- unitlab/cloudflare_api_tunnel.py +74 -26
- {unitlab-2.3.17.dist-info → unitlab-2.3.20.dist-info}/METADATA +1 -1
- {unitlab-2.3.17.dist-info → unitlab-2.3.20.dist-info}/RECORD +7 -7
- {unitlab-2.3.17.dist-info → unitlab-2.3.20.dist-info}/LICENSE.md +0 -0
- {unitlab-2.3.17.dist-info → unitlab-2.3.20.dist-info}/WHEEL +0 -0
- {unitlab-2.3.17.dist-info → unitlab-2.3.20.dist-info}/entry_points.txt +0 -0
- {unitlab-2.3.17.dist-info → unitlab-2.3.20.dist-info}/top_level.txt +0 -0
unitlab/cloudflare_api_tunnel.py
CHANGED
@@ -206,7 +206,7 @@ class CloudflareAPITunnel:
|
|
206
206
|
print(" Assuming routes are configured in dashboard.")
|
207
207
|
return True
|
208
208
|
|
209
|
-
def create_device_tunnel(self):
|
209
|
+
def create_device_tunnel(self, retry_on_fail=True):
|
210
210
|
"""
|
211
211
|
Create a unique tunnel for this device if it doesn't exist
|
212
212
|
"""
|
@@ -372,11 +372,26 @@ class CloudflareAPITunnel:
|
|
372
372
|
self.create_dns_records()
|
373
373
|
self.update_tunnel_config()
|
374
374
|
|
375
|
+
# Check if we need to recreate the tunnel
|
376
|
+
if device_tunnel and device_tunnel.get('needs_recreation'):
|
377
|
+
print("🔄 Recreating tunnel due to access issues...")
|
378
|
+
# Delete the old tunnel
|
379
|
+
tunnel_id = device_tunnel['id']
|
380
|
+
delete_url = f"{self.api_base}/accounts/{self.account_id}/tunnels/{tunnel_id}"
|
381
|
+
delete_response = requests.delete(delete_url, headers=self.headers)
|
382
|
+
if delete_response.status_code in [200, 204, 404]:
|
383
|
+
print(f" ✅ Deleted old tunnel")
|
384
|
+
# Try creating a new one with a timestamp suffix
|
385
|
+
self.clean_device_id = f"{self.device_id.replace(' ', '').replace('-', '').replace('.', '').replace('_', '')[:24]}-{int(time.time())}"
|
386
|
+
device_tunnel = self.create_device_tunnel(retry_on_fail=False)
|
387
|
+
if device_tunnel:
|
388
|
+
print(f" ✅ Created new tunnel")
|
389
|
+
|
375
390
|
if not device_tunnel:
|
376
391
|
print("❌ Could not create/find device tunnel")
|
377
392
|
# Fallback to shared tunnel if API fails
|
378
393
|
print("⚠️ Falling back to shared tunnel...")
|
379
|
-
service_token = "
|
394
|
+
service_token = "eyJhIjoiYzkxMTkyYWUyMGE1ZDQzZjY1ZTA4NzU1MGQ4ZGM4OWIiLCJ0IjoiMDc3N2ZjMTAtNDljNC00NzJkLTg2NjEtZjYwZDgwZDYxODRkIiwicyI6Ik9XRTNaak5tTVdVdE1tWTRaUzAwTmpoakLTazBmalF0WXpjek1tSm1ZVGt4WlRRMCJ9"
|
380
395
|
cmd = [
|
381
396
|
cloudflared_path,
|
382
397
|
"tunnel",
|
@@ -402,12 +417,22 @@ class CloudflareAPITunnel:
|
|
402
417
|
self._save_tunnel_credentials(device_tunnel)
|
403
418
|
# Continue to use credentials below
|
404
419
|
else:
|
405
|
-
print("⚠️ No stored credentials,
|
420
|
+
print("⚠️ No stored credentials, requesting tunnel token...")
|
406
421
|
# Get token for this tunnel
|
407
422
|
token_url = f"{self.api_base}/accounts/{self.account_id}/tunnels/{tunnel_id}/token"
|
423
|
+
print(f" Token URL: {token_url}")
|
424
|
+
|
408
425
|
token_response = requests.get(token_url, headers=self.headers)
|
426
|
+
print(f" Token response status: {token_response.status_code}")
|
427
|
+
|
409
428
|
if token_response.status_code == 200:
|
410
|
-
|
429
|
+
result = token_response.json()
|
430
|
+
if 'result' in result:
|
431
|
+
token = result['result']
|
432
|
+
print(f" ✅ Got tunnel token")
|
433
|
+
else:
|
434
|
+
print(f" ❌ No token in response: {result}")
|
435
|
+
return None
|
411
436
|
|
412
437
|
# Check if config file exists with ingress rules
|
413
438
|
if config_file.exists():
|
@@ -432,8 +457,21 @@ class CloudflareAPITunnel:
|
|
432
457
|
"--token", token
|
433
458
|
]
|
434
459
|
else:
|
435
|
-
print("❌ Could not get tunnel token")
|
436
|
-
|
460
|
+
print(f"❌ Could not get tunnel token: {token_response.status_code}")
|
461
|
+
if token_response.text:
|
462
|
+
print(f" Error: {token_response.text}")
|
463
|
+
|
464
|
+
# If 404, tunnel doesn't exist or we don't have access
|
465
|
+
if token_response.status_code == 404:
|
466
|
+
print(f"🔄 Tunnel not accessible (404), will recreate...")
|
467
|
+
# Mark for recreation and continue
|
468
|
+
device_tunnel['needs_recreation'] = True
|
469
|
+
# Set cmd to None to skip the rest of this block
|
470
|
+
cmd = None
|
471
|
+
else:
|
472
|
+
print(f" ℹ️ Token endpoint returned {token_response.status_code}")
|
473
|
+
print(f" This might mean the tunnel needs to be recreated manually")
|
474
|
+
return None
|
437
475
|
|
438
476
|
# Only use credentials if we haven't already set cmd with token
|
439
477
|
if cmd is None and creds_file.exists():
|
@@ -458,27 +496,37 @@ class CloudflareAPITunnel:
|
|
458
496
|
tunnel_id
|
459
497
|
]
|
460
498
|
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
499
|
+
# Only start the tunnel if we have a valid command
|
500
|
+
if cmd:
|
501
|
+
self.tunnel_process = subprocess.Popen(
|
502
|
+
cmd,
|
503
|
+
stdout=subprocess.PIPE,
|
504
|
+
stderr=subprocess.STDOUT,
|
505
|
+
text=True,
|
506
|
+
bufsize=1
|
507
|
+
)
|
508
|
+
|
509
|
+
print("⏳ Waiting for tunnel to connect...")
|
510
|
+
time.sleep(5)
|
511
|
+
|
512
|
+
if self.tunnel_process.poll() is None:
|
513
|
+
print("✅ Tunnel is running!")
|
514
|
+
print(f"📌 Device ID: {self.clean_device_id}")
|
515
|
+
print(f"📌 Jupyter URL: {self.jupyter_url}")
|
516
|
+
print(f"📌 SSH hostname: {self.ssh_hostname}")
|
517
|
+
print(f"📌 SSH command: ssh -o ProxyCommand='cloudflared access ssh --hostname {self.ssh_hostname}' user@localhost")
|
518
|
+
return self.tunnel_process
|
519
|
+
else:
|
520
|
+
output = self.tunnel_process.stdout.read() if self.tunnel_process.stdout else ""
|
521
|
+
print(f"❌ Tunnel failed to start: {output}")
|
522
|
+
return None
|
479
523
|
else:
|
480
|
-
|
481
|
-
|
524
|
+
# If no cmd, it means we need to handle recreation or other error
|
525
|
+
if device_tunnel and device_tunnel.get('needs_recreation'):
|
526
|
+
# The recreation happens above before this point
|
527
|
+
print("❌ Failed to start tunnel after recreation attempt")
|
528
|
+
else:
|
529
|
+
print("❌ No valid command to start tunnel")
|
482
530
|
return None
|
483
531
|
|
484
532
|
except Exception as e:
|
@@ -2,15 +2,15 @@ unitlab/__init__.py,sha256=Wtk5kQ_MTlxtd3mxJIn2qHVK5URrVcasMMPjD3BtrVM,214
|
|
2
2
|
unitlab/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
|
3
3
|
unitlab/binary_manager.py,sha256=Q1v2Odm0hk_3g7jfDUJQfkjEbUbSjtuyo2JDUyWjDrk,5468
|
4
4
|
unitlab/client.py,sha256=V5fTgbprmMsnMwD_FPn7oZh0KK6hdnqB4BYuY4D-JRw,24558
|
5
|
-
unitlab/cloudflare_api_tunnel.py,sha256=
|
5
|
+
unitlab/cloudflare_api_tunnel.py,sha256=RyjjV3jeterHtWGnNZe7ABJhFH1cYlweWE53-n29OF8,28600
|
6
6
|
unitlab/exceptions.py,sha256=68Tr6LreEzjQ3Vns8HAaWdtewtkNUJOvPazbf6NSnXU,950
|
7
7
|
unitlab/main.py,sha256=7gPZ_2n90sxDnq9oGZVKOkuifr-k7w2Tq3ZIldAUE8I,5877
|
8
8
|
unitlab/tunnel_config.py,sha256=7CiAqasfg26YQfJYXapCBQPSoqw4jIx6yR64saybLLo,8312
|
9
9
|
unitlab/tunnel_service_token.py,sha256=ji96a4s4W2cFJrHZle0zBD85Ac_T862-gCKzBUomrxM,3125
|
10
10
|
unitlab/utils.py,sha256=83ekAxxfXecFTg76Z62BGDybC_skKJHYoLyawCD9wGM,1920
|
11
|
-
unitlab-2.3.
|
12
|
-
unitlab-2.3.
|
13
|
-
unitlab-2.3.
|
14
|
-
unitlab-2.3.
|
15
|
-
unitlab-2.3.
|
16
|
-
unitlab-2.3.
|
11
|
+
unitlab-2.3.20.dist-info/LICENSE.md,sha256=Gn7RRvByorAcAaM-WbyUpsgi5ED1-bKFFshbWfYYz2Y,1069
|
12
|
+
unitlab-2.3.20.dist-info/METADATA,sha256=ZeA9clkXoaXSJgOKLXwJuXVLIPm0flHf0D6aTdFRDcY,844
|
13
|
+
unitlab-2.3.20.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
|
14
|
+
unitlab-2.3.20.dist-info/entry_points.txt,sha256=ig-PjKEqSCj3UTdyANgEi4tsAU84DyXdaOJ02NHX4bY,45
|
15
|
+
unitlab-2.3.20.dist-info/top_level.txt,sha256=Al4ZlTYE3fTJK2o6YLCDMH5_DjuQkffRBMxgmWbKaqQ,8
|
16
|
+
unitlab-2.3.20.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|