gitarsenal-cli 1.6.8 → 1.6.10
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.
package/package.json
CHANGED
@@ -0,0 +1,54 @@
|
|
1
|
+
import subprocess
|
2
|
+
import time
|
3
|
+
import secrets
|
4
|
+
import string
|
5
|
+
import modal
|
6
|
+
|
7
|
+
def generate_random_password(length=16):
|
8
|
+
"""Generate a random password for SSH access"""
|
9
|
+
alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
|
10
|
+
password = ''.join(secrets.choice(alphabet) for i in range(length))
|
11
|
+
return password
|
12
|
+
|
13
|
+
image = (
|
14
|
+
modal.Image.from_registry("nvidia/cuda:12.4.0-devel-ubuntu22.04", add_python="3.11")
|
15
|
+
.apt_install("openssh-server", "sudo", "curl", "wget", "git", "vim", "htop", "tmux", "nvtop")
|
16
|
+
.pip_install("cupy-cuda12x", "torch", "transformers")
|
17
|
+
.run_commands(
|
18
|
+
"mkdir -p /var/run/sshd",
|
19
|
+
"ssh-keygen -A",
|
20
|
+
"sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config",
|
21
|
+
"sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
|
22
|
+
"echo 'export PATH=/usr/local/cuda/bin:$PATH' >> /root/.bashrc"
|
23
|
+
)
|
24
|
+
)
|
25
|
+
|
26
|
+
app = modal.App("cuda-ssh-container", image=image)
|
27
|
+
|
28
|
+
@app.function(gpu="A10G", timeout=3600)
|
29
|
+
def start_ssh():
|
30
|
+
# Generate SSH password
|
31
|
+
password = generate_random_password()
|
32
|
+
subprocess.run(["bash", "-c", f"echo 'root:{password}' | chpasswd"], check=True)
|
33
|
+
|
34
|
+
# Start SSH server
|
35
|
+
subprocess.Popen(["/usr/sbin/sshd", "-D"])
|
36
|
+
time.sleep(2)
|
37
|
+
|
38
|
+
# Test CUDA
|
39
|
+
subprocess.run(["nvidia-smi"])
|
40
|
+
subprocess.run(["nvcc", "--version"])
|
41
|
+
|
42
|
+
# Forward SSH port
|
43
|
+
with modal.forward(port=22, unencrypted=True) as tunnel:
|
44
|
+
hostname, port = tunnel.tcp_socket
|
45
|
+
print(f"SSH: ssh -p {port} root@{hostname}")
|
46
|
+
print(f"Password: {password}")
|
47
|
+
|
48
|
+
# Keep alive
|
49
|
+
while True:
|
50
|
+
time.sleep(60)
|
51
|
+
|
52
|
+
if __name__ == "__main__":
|
53
|
+
with app.run():
|
54
|
+
start_ssh.remote()
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import subprocess
|
2
|
+
import time
|
3
|
+
import secrets
|
4
|
+
import string
|
5
|
+
import modal
|
6
|
+
|
7
|
+
def generate_random_password(length=16):
|
8
|
+
"""Generate a random password for SSH access"""
|
9
|
+
alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
|
10
|
+
password = ''.join(secrets.choice(alphabet) for i in range(length))
|
11
|
+
return password
|
12
|
+
|
13
|
+
image = (
|
14
|
+
modal.Image.from_registry("nvidia/cuda:12.4.0-devel-ubuntu22.04", add_python="3.11")
|
15
|
+
.apt_install("openssh-server", "sudo", "curl", "wget", "git", "vim", "htop", "tmux", "nvtop")
|
16
|
+
.pip_install("cupy-cuda12x", "torch", "transformers")
|
17
|
+
.run_commands(
|
18
|
+
"mkdir -p /var/run/sshd",
|
19
|
+
"ssh-keygen -A",
|
20
|
+
"sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config",
|
21
|
+
"sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
|
22
|
+
"echo 'export PATH=/usr/local/cuda/bin:$PATH' >> /root/.bashrc"
|
23
|
+
)
|
24
|
+
)
|
25
|
+
|
26
|
+
app = modal.App("cuda-ssh-container", image=image)
|
27
|
+
|
28
|
+
@app.function(gpu="A10G", timeout=3600)
|
29
|
+
def start_ssh():
|
30
|
+
# Generate SSH password
|
31
|
+
password = generate_random_password()
|
32
|
+
subprocess.run(["bash", "-c", f"echo 'root:{password}' | chpasswd"], check=True)
|
33
|
+
|
34
|
+
# Start SSH server
|
35
|
+
subprocess.Popen(["/usr/sbin/sshd", "-D"])
|
36
|
+
time.sleep(2)
|
37
|
+
|
38
|
+
# Test CUDA
|
39
|
+
subprocess.run(["nvidia-smi"])
|
40
|
+
subprocess.run(["nvcc", "--version"])
|
41
|
+
|
42
|
+
# Forward SSH port
|
43
|
+
with modal.forward(port=22, unencrypted=True) as tunnel:
|
44
|
+
hostname, port = tunnel.tcp_socket
|
45
|
+
print(f"SSH: ssh -p {port} root@{hostname}")
|
46
|
+
print(f"Password: {password}")
|
47
|
+
|
48
|
+
# Keep alive
|
49
|
+
while True:
|
50
|
+
time.sleep(60)
|
51
|
+
|
52
|
+
if __name__ == "__main__":
|
53
|
+
with app.run():
|
54
|
+
start_ssh.remote()
|
@@ -1183,23 +1183,22 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
1183
1183
|
# Create SSH-enabled image
|
1184
1184
|
try:
|
1185
1185
|
print("📦 Building SSH-enabled image...")
|
1186
|
+
|
1187
|
+
# Use a more stable CUDA base image and avoid problematic packages
|
1186
1188
|
ssh_image = (
|
1187
|
-
modal.Image.from_registry("nvidia/cuda:12.4.0-
|
1189
|
+
modal.Image.from_registry("nvidia/cuda:12.4.0-runtime-ubuntu22.04", add_python="3.11")
|
1188
1190
|
.apt_install(
|
1189
1191
|
"openssh-server", "sudo", "curl", "wget", "vim", "htop", "git",
|
1190
1192
|
"python3", "python3-pip", "build-essential", "tmux", "screen", "nano",
|
1191
|
-
"gpg", "ca-certificates", "software-properties-common"
|
1193
|
+
"gpg", "ca-certificates", "software-properties-common"
|
1192
1194
|
)
|
1193
|
-
.pip_install("uv", "modal", "requests", "openai") #
|
1195
|
+
.pip_install("uv", "modal", "requests", "openai") # Remove problematic CUDA packages
|
1194
1196
|
.run_commands(
|
1195
1197
|
# Create SSH directory
|
1196
1198
|
"mkdir -p /var/run/sshd",
|
1197
1199
|
"mkdir -p /root/.ssh",
|
1198
1200
|
"chmod 700 /root/.ssh",
|
1199
1201
|
|
1200
|
-
# Generate SSH host keys
|
1201
|
-
"ssh-keygen -A",
|
1202
|
-
|
1203
1202
|
# Configure SSH server
|
1204
1203
|
"sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config",
|
1205
1204
|
"sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
|
@@ -1209,9 +1208,8 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
1209
1208
|
"echo 'ClientAliveInterval 60' >> /etc/ssh/sshd_config",
|
1210
1209
|
"echo 'ClientAliveCountMax 3' >> /etc/ssh/sshd_config",
|
1211
1210
|
|
1212
|
-
#
|
1213
|
-
"
|
1214
|
-
"echo 'export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH' >> /root/.bashrc",
|
1211
|
+
# Generate SSH host keys
|
1212
|
+
"ssh-keygen -A",
|
1215
1213
|
|
1216
1214
|
# Set up a nice bash prompt
|
1217
1215
|
"echo 'export PS1=\"\\[\\e[1;32m\\]modal:\\[\\e[1;34m\\]\\w\\[\\e[0m\\]$ \"' >> /root/.bashrc",
|
@@ -1226,18 +1224,25 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
1226
1224
|
volumes_config = {}
|
1227
1225
|
if volume:
|
1228
1226
|
volumes_config[volume_mount_path] = volume
|
1227
|
+
|
1228
|
+
# Create app with image passed directly (THIS IS THE KEY CHANGE)
|
1229
|
+
try:
|
1230
|
+
print("🔍 Testing app creation...")
|
1231
|
+
app = modal.App(app_name, image=ssh_image) # Pass image here
|
1232
|
+
print("✅ Created app successfully")
|
1233
|
+
except Exception as e:
|
1234
|
+
print(f"❌ Error creating app: {e}")
|
1235
|
+
return None
|
1229
1236
|
|
1230
|
-
# Define the SSH container function
|
1237
|
+
# Define the SSH container function (remove image from decorator)
|
1231
1238
|
@app.function(
|
1232
|
-
image=ssh_image,
|
1233
1239
|
timeout=timeout_minutes * 60, # Convert to seconds
|
1234
1240
|
gpu=gpu_spec['gpu'],
|
1235
1241
|
cpu=2,
|
1236
|
-
memory=8192,
|
1237
1242
|
serialized=True,
|
1238
1243
|
volumes=volumes_config if volumes_config else None,
|
1239
1244
|
)
|
1240
|
-
def
|
1245
|
+
def ssh_container_function(ssh_password=None, repo_url=None, repo_name=None, setup_commands=None, openai_api_key=None):
|
1241
1246
|
"""Start SSH container with password authentication and optional setup."""
|
1242
1247
|
import subprocess
|
1243
1248
|
import time
|
@@ -1247,25 +1252,14 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
1247
1252
|
subprocess.run(["bash", "-c", f"echo 'root:{ssh_password}' | chpasswd"], check=True)
|
1248
1253
|
|
1249
1254
|
# Set OpenAI API key if provided
|
1250
|
-
openai_api_key = os.environ.get("OPENAI_API_KEY")
|
1251
1255
|
if openai_api_key:
|
1252
1256
|
os.environ['OPENAI_API_KEY'] = openai_api_key
|
1253
1257
|
print(f"✅ Set OpenAI API key in container environment (length: {len(openai_api_key)})")
|
1254
1258
|
else:
|
1255
1259
|
print("⚠️ No OpenAI API key provided to container")
|
1256
1260
|
|
1257
|
-
# Start SSH service
|
1258
|
-
subprocess.
|
1259
|
-
time.sleep(2) # Give SSH time to start
|
1260
|
-
|
1261
|
-
# Test CUDA setup
|
1262
|
-
try:
|
1263
|
-
print("🔧 Testing CUDA setup...")
|
1264
|
-
subprocess.run(["nvidia-smi"], check=True)
|
1265
|
-
subprocess.run(["nvcc", "--version"], check=True)
|
1266
|
-
print("✅ CUDA setup verified")
|
1267
|
-
except subprocess.CalledProcessError as e:
|
1268
|
-
print(f"⚠️ CUDA test failed: {e}")
|
1261
|
+
# Start SSH service
|
1262
|
+
subprocess.run(["service", "ssh", "start"], check=True)
|
1269
1263
|
|
1270
1264
|
# Clone repository if provided
|
1271
1265
|
if repo_url:
|
@@ -1393,28 +1387,43 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
1393
1387
|
print(f"🔍 Analyzing directory structure after failed cd command...")
|
1394
1388
|
subprocess.run("pwd && ls -la && echo '--- Parent directory ---' && ls -la ..", shell=True, check=False)
|
1395
1389
|
|
1396
|
-
#
|
1397
|
-
with modal.forward(
|
1398
|
-
|
1390
|
+
# Create SSH tunnel
|
1391
|
+
with modal.forward(22, unencrypted=True) as tunnel:
|
1392
|
+
host, port = tunnel.tcp_socket
|
1399
1393
|
|
1400
1394
|
print("\n" + "=" * 80)
|
1401
1395
|
print("🎉 SSH CONTAINER IS READY!")
|
1402
1396
|
print("=" * 80)
|
1403
|
-
print(f"SSH:
|
1404
|
-
print(f"
|
1397
|
+
print(f"🌐 SSH Host: {host}")
|
1398
|
+
print(f"🔌 SSH Port: {port}")
|
1399
|
+
print(f"👤 Username: root")
|
1400
|
+
print(f"🔐 Password: {ssh_password}")
|
1401
|
+
print()
|
1402
|
+
print("🔗 CONNECT USING THIS COMMAND:")
|
1403
|
+
print(f"ssh -p {port} root@{host}")
|
1405
1404
|
print("=" * 80)
|
1406
1405
|
|
1407
|
-
# Keep
|
1406
|
+
# Keep the container running
|
1408
1407
|
while True:
|
1409
|
-
time.sleep(
|
1408
|
+
time.sleep(30)
|
1409
|
+
# Check if SSH service is still running
|
1410
|
+
try:
|
1411
|
+
subprocess.run(["service", "ssh", "status"], check=True,
|
1412
|
+
capture_output=True)
|
1413
|
+
except subprocess.CalledProcessError:
|
1414
|
+
print("⚠️ SSH service stopped, restarting...")
|
1415
|
+
subprocess.run(["service", "ssh", "start"], check=True)
|
1410
1416
|
|
1411
1417
|
# Run the container
|
1412
1418
|
try:
|
1413
1419
|
print("⏳ Starting container... This may take 1-2 minutes...")
|
1414
1420
|
|
1415
|
-
# Start the container
|
1416
|
-
with
|
1417
|
-
|
1421
|
+
# Start the container in a new thread to avoid blocking
|
1422
|
+
with modal.enable_output():
|
1423
|
+
with app.run():
|
1424
|
+
# Get the API key from environment
|
1425
|
+
api_key = os.environ.get("OPENAI_API_KEY")
|
1426
|
+
ssh_container_function.remote(ssh_password, repo_url, repo_name, setup_commands, api_key)
|
1418
1427
|
|
1419
1428
|
# Clean up Modal token after container is successfully created
|
1420
1429
|
cleanup_modal_token()
|
@@ -1183,23 +1183,22 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
1183
1183
|
# Create SSH-enabled image
|
1184
1184
|
try:
|
1185
1185
|
print("📦 Building SSH-enabled image...")
|
1186
|
+
|
1187
|
+
# Use a more stable CUDA base image and avoid problematic packages
|
1186
1188
|
ssh_image = (
|
1187
|
-
modal.Image.from_registry("nvidia/cuda:12.4.0-
|
1189
|
+
modal.Image.from_registry("nvidia/cuda:12.4.0-runtime-ubuntu22.04", add_python="3.11")
|
1188
1190
|
.apt_install(
|
1189
1191
|
"openssh-server", "sudo", "curl", "wget", "vim", "htop", "git",
|
1190
1192
|
"python3", "python3-pip", "build-essential", "tmux", "screen", "nano",
|
1191
|
-
"gpg", "ca-certificates", "software-properties-common"
|
1193
|
+
"gpg", "ca-certificates", "software-properties-common"
|
1192
1194
|
)
|
1193
|
-
.pip_install("uv", "modal", "requests", "openai") #
|
1195
|
+
.pip_install("uv", "modal", "requests", "openai") # Remove problematic CUDA packages
|
1194
1196
|
.run_commands(
|
1195
1197
|
# Create SSH directory
|
1196
1198
|
"mkdir -p /var/run/sshd",
|
1197
1199
|
"mkdir -p /root/.ssh",
|
1198
1200
|
"chmod 700 /root/.ssh",
|
1199
1201
|
|
1200
|
-
# Generate SSH host keys
|
1201
|
-
"ssh-keygen -A",
|
1202
|
-
|
1203
1202
|
# Configure SSH server
|
1204
1203
|
"sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config",
|
1205
1204
|
"sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
|
@@ -1209,9 +1208,8 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
1209
1208
|
"echo 'ClientAliveInterval 60' >> /etc/ssh/sshd_config",
|
1210
1209
|
"echo 'ClientAliveCountMax 3' >> /etc/ssh/sshd_config",
|
1211
1210
|
|
1212
|
-
#
|
1213
|
-
"
|
1214
|
-
"echo 'export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH' >> /root/.bashrc",
|
1211
|
+
# Generate SSH host keys
|
1212
|
+
"ssh-keygen -A",
|
1215
1213
|
|
1216
1214
|
# Set up a nice bash prompt
|
1217
1215
|
"echo 'export PS1=\"\\[\\e[1;32m\\]modal:\\[\\e[1;34m\\]\\w\\[\\e[0m\\]$ \"' >> /root/.bashrc",
|
@@ -1226,18 +1224,25 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
1226
1224
|
volumes_config = {}
|
1227
1225
|
if volume:
|
1228
1226
|
volumes_config[volume_mount_path] = volume
|
1227
|
+
|
1228
|
+
# Create app with image passed directly (THIS IS THE KEY CHANGE)
|
1229
|
+
try:
|
1230
|
+
print("🔍 Testing app creation...")
|
1231
|
+
app = modal.App(app_name, image=ssh_image) # Pass image here
|
1232
|
+
print("✅ Created app successfully")
|
1233
|
+
except Exception as e:
|
1234
|
+
print(f"❌ Error creating app: {e}")
|
1235
|
+
return None
|
1229
1236
|
|
1230
|
-
# Define the SSH container function
|
1237
|
+
# Define the SSH container function (remove image from decorator)
|
1231
1238
|
@app.function(
|
1232
|
-
image=ssh_image,
|
1233
1239
|
timeout=timeout_minutes * 60, # Convert to seconds
|
1234
1240
|
gpu=gpu_spec['gpu'],
|
1235
1241
|
cpu=2,
|
1236
|
-
memory=8192,
|
1237
1242
|
serialized=True,
|
1238
1243
|
volumes=volumes_config if volumes_config else None,
|
1239
1244
|
)
|
1240
|
-
def
|
1245
|
+
def ssh_container_function(ssh_password=None, repo_url=None, repo_name=None, setup_commands=None, openai_api_key=None):
|
1241
1246
|
"""Start SSH container with password authentication and optional setup."""
|
1242
1247
|
import subprocess
|
1243
1248
|
import time
|
@@ -1247,25 +1252,14 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
1247
1252
|
subprocess.run(["bash", "-c", f"echo 'root:{ssh_password}' | chpasswd"], check=True)
|
1248
1253
|
|
1249
1254
|
# Set OpenAI API key if provided
|
1250
|
-
openai_api_key = os.environ.get("OPENAI_API_KEY")
|
1251
1255
|
if openai_api_key:
|
1252
1256
|
os.environ['OPENAI_API_KEY'] = openai_api_key
|
1253
1257
|
print(f"✅ Set OpenAI API key in container environment (length: {len(openai_api_key)})")
|
1254
1258
|
else:
|
1255
1259
|
print("⚠️ No OpenAI API key provided to container")
|
1256
1260
|
|
1257
|
-
# Start SSH service
|
1258
|
-
subprocess.
|
1259
|
-
time.sleep(2) # Give SSH time to start
|
1260
|
-
|
1261
|
-
# Test CUDA setup
|
1262
|
-
try:
|
1263
|
-
print("🔧 Testing CUDA setup...")
|
1264
|
-
subprocess.run(["nvidia-smi"], check=True)
|
1265
|
-
subprocess.run(["nvcc", "--version"], check=True)
|
1266
|
-
print("✅ CUDA setup verified")
|
1267
|
-
except subprocess.CalledProcessError as e:
|
1268
|
-
print(f"⚠️ CUDA test failed: {e}")
|
1261
|
+
# Start SSH service
|
1262
|
+
subprocess.run(["service", "ssh", "start"], check=True)
|
1269
1263
|
|
1270
1264
|
# Clone repository if provided
|
1271
1265
|
if repo_url:
|
@@ -1393,28 +1387,43 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
1393
1387
|
print(f"🔍 Analyzing directory structure after failed cd command...")
|
1394
1388
|
subprocess.run("pwd && ls -la && echo '--- Parent directory ---' && ls -la ..", shell=True, check=False)
|
1395
1389
|
|
1396
|
-
#
|
1397
|
-
with modal.forward(
|
1398
|
-
|
1390
|
+
# Create SSH tunnel
|
1391
|
+
with modal.forward(22, unencrypted=True) as tunnel:
|
1392
|
+
host, port = tunnel.tcp_socket
|
1399
1393
|
|
1400
1394
|
print("\n" + "=" * 80)
|
1401
1395
|
print("🎉 SSH CONTAINER IS READY!")
|
1402
1396
|
print("=" * 80)
|
1403
|
-
print(f"SSH:
|
1404
|
-
print(f"
|
1397
|
+
print(f"🌐 SSH Host: {host}")
|
1398
|
+
print(f"🔌 SSH Port: {port}")
|
1399
|
+
print(f"👤 Username: root")
|
1400
|
+
print(f"🔐 Password: {ssh_password}")
|
1401
|
+
print()
|
1402
|
+
print("🔗 CONNECT USING THIS COMMAND:")
|
1403
|
+
print(f"ssh -p {port} root@{host}")
|
1405
1404
|
print("=" * 80)
|
1406
1405
|
|
1407
|
-
# Keep
|
1406
|
+
# Keep the container running
|
1408
1407
|
while True:
|
1409
|
-
time.sleep(
|
1408
|
+
time.sleep(30)
|
1409
|
+
# Check if SSH service is still running
|
1410
|
+
try:
|
1411
|
+
subprocess.run(["service", "ssh", "status"], check=True,
|
1412
|
+
capture_output=True)
|
1413
|
+
except subprocess.CalledProcessError:
|
1414
|
+
print("⚠️ SSH service stopped, restarting...")
|
1415
|
+
subprocess.run(["service", "ssh", "start"], check=True)
|
1410
1416
|
|
1411
1417
|
# Run the container
|
1412
1418
|
try:
|
1413
1419
|
print("⏳ Starting container... This may take 1-2 minutes...")
|
1414
1420
|
|
1415
|
-
# Start the container
|
1416
|
-
with
|
1417
|
-
|
1421
|
+
# Start the container in a new thread to avoid blocking
|
1422
|
+
with modal.enable_output():
|
1423
|
+
with app.run():
|
1424
|
+
# Get the API key from environment
|
1425
|
+
api_key = os.environ.get("OPENAI_API_KEY")
|
1426
|
+
ssh_container_function.remote(ssh_password, repo_url, repo_name, setup_commands, api_key)
|
1418
1427
|
|
1419
1428
|
# Clean up Modal token after container is successfully created
|
1420
1429
|
cleanup_modal_token()
|