gitarsenal-cli 1.1.7 ā 1.1.8
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
Binary file
|
Binary file
|
@@ -82,30 +82,32 @@ def setup_modal_auth():
|
|
82
82
|
try:
|
83
83
|
# Set the token in the environment
|
84
84
|
os.environ["MODAL_TOKEN_ID"] = MODAL_TOKEN
|
85
|
-
# Also set the token directly in modal.config
|
86
|
-
modal.config._auth_config.token_id = MODAL_TOKEN
|
87
85
|
|
88
|
-
#
|
86
|
+
# Try to set the token using Modal CLI
|
89
87
|
try:
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
try:
|
97
|
-
import subprocess
|
98
|
-
result = subprocess.run(
|
99
|
-
["modal", "token", "set", MODAL_TOKEN],
|
100
|
-
capture_output=True, text=True, check=True
|
101
|
-
)
|
88
|
+
import subprocess
|
89
|
+
result = subprocess.run(
|
90
|
+
["modal", "token", "set", MODAL_TOKEN],
|
91
|
+
capture_output=True, text=True, check=False
|
92
|
+
)
|
93
|
+
if result.returncode == 0:
|
102
94
|
logger.info("Modal token set via CLI command")
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
95
|
+
else:
|
96
|
+
logger.warning(f"CLI token set returned: {result.stderr}")
|
97
|
+
except Exception as cli_err:
|
98
|
+
logger.warning(f"Failed to set token via CLI: {cli_err}")
|
107
99
|
|
108
|
-
|
100
|
+
# Verify token is working by attempting a simple operation
|
101
|
+
try:
|
102
|
+
# Try a simple Modal operation
|
103
|
+
import modal.cli.app_create
|
104
|
+
logger.info("Modal module imported successfully")
|
105
|
+
return True
|
106
|
+
except Exception as e:
|
107
|
+
logger.error(f"Error importing Modal module: {e}")
|
108
|
+
return False
|
109
|
+
|
110
|
+
logger.info("Modal token set in environment")
|
109
111
|
return True
|
110
112
|
except Exception as e:
|
111
113
|
logger.error(f"Error setting up Modal authentication: {e}")
|
@@ -201,10 +203,30 @@ def create_ssh_container():
|
|
201
203
|
if not authenticate_request():
|
202
204
|
return jsonify({"error": "Unauthorized"}), 401
|
203
205
|
|
204
|
-
if
|
205
|
-
|
206
|
+
# Check if Modal token is available
|
207
|
+
if not MODAL_TOKEN:
|
208
|
+
logger.error("Cannot create SSH container: No Modal token available")
|
209
|
+
return jsonify({"error": "Modal token not configured on server"}), 500
|
206
210
|
|
207
211
|
try:
|
212
|
+
# Set the token directly in environment
|
213
|
+
os.environ["MODAL_TOKEN_ID"] = MODAL_TOKEN
|
214
|
+
logger.info(f"Set MODAL_TOKEN_ID in environment (length: {len(MODAL_TOKEN)})")
|
215
|
+
|
216
|
+
# Try to set token via CLI as well
|
217
|
+
try:
|
218
|
+
import subprocess
|
219
|
+
result = subprocess.run(
|
220
|
+
["modal", "token", "set", MODAL_TOKEN],
|
221
|
+
capture_output=True, text=True
|
222
|
+
)
|
223
|
+
if result.returncode == 0:
|
224
|
+
logger.info("Successfully set token via Modal CLI")
|
225
|
+
else:
|
226
|
+
logger.warning(f"Modal CLI token set returned: {result.stderr}")
|
227
|
+
except Exception as e:
|
228
|
+
logger.warning(f"Failed to set token via CLI: {e}")
|
229
|
+
|
208
230
|
data = request.json
|
209
231
|
gpu_type = data.get('gpu_type', 'A10G')
|
210
232
|
repo_url = data.get('repo_url')
|
@@ -229,39 +251,12 @@ def create_ssh_container():
|
|
229
251
|
logger.info(f"Modal token status before thread: {token_status}")
|
230
252
|
logger.info(f"Modal token length: {len(MODAL_TOKEN) if MODAL_TOKEN else 'N/A'}")
|
231
253
|
|
232
|
-
# Ensure Modal token is set in environment
|
233
|
-
if MODAL_TOKEN:
|
234
|
-
os.environ["MODAL_TOKEN_ID"] = MODAL_TOKEN
|
235
|
-
# Also set directly in modal.config
|
236
|
-
modal.config._auth_config.token_id = MODAL_TOKEN
|
237
|
-
logger.info("Modal token explicitly set in environment and config")
|
238
|
-
|
239
254
|
# Start container creation in a separate thread
|
240
255
|
def create_container_thread():
|
241
256
|
try:
|
242
|
-
#
|
243
|
-
if not setup_modal_auth():
|
244
|
-
logger.error("Failed to set up Modal authentication in thread")
|
245
|
-
return
|
246
|
-
|
247
|
-
# Explicitly set the Modal token in the environment again for this thread
|
257
|
+
# Set token in environment for this thread
|
248
258
|
os.environ["MODAL_TOKEN_ID"] = MODAL_TOKEN
|
249
|
-
|
250
|
-
# Directly set the token in Modal config
|
251
|
-
modal.config._auth_config.token_id = MODAL_TOKEN
|
252
|
-
|
253
|
-
# Log token status for debugging
|
254
|
-
token_status = "Token is set" if MODAL_TOKEN else "Token is missing"
|
255
|
-
logger.info(f"Modal token status in thread: {token_status}")
|
256
|
-
logger.info(f"Modal token length in thread: {len(MODAL_TOKEN) if MODAL_TOKEN else 'N/A'}")
|
257
|
-
|
258
|
-
# Verify token is actually working
|
259
|
-
try:
|
260
|
-
# Try a simple Modal operation to verify authentication
|
261
|
-
import modal.cli.app_create
|
262
|
-
logger.info("Modal module imported successfully in thread")
|
263
|
-
except Exception as auth_err:
|
264
|
-
logger.error(f"Modal authentication verification error: {auth_err}")
|
259
|
+
logger.info(f"Set MODAL_TOKEN_ID in thread (length: {len(MODAL_TOKEN)})")
|
265
260
|
|
266
261
|
result = create_modal_ssh_container(
|
267
262
|
gpu_type,
|
@@ -2075,47 +2075,44 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
2075
2075
|
modal_token = os.environ.get("MODAL_TOKEN_ID")
|
2076
2076
|
if not modal_token:
|
2077
2077
|
print("ā ļø MODAL_TOKEN_ID not found in environment.")
|
2078
|
-
# Try to
|
2078
|
+
# Try to get from MODAL_TOKEN
|
2079
|
+
modal_token = os.environ.get("MODAL_TOKEN")
|
2080
|
+
if modal_token:
|
2081
|
+
print("ā
Found token in MODAL_TOKEN environment variable")
|
2082
|
+
os.environ["MODAL_TOKEN_ID"] = modal_token
|
2083
|
+
|
2084
|
+
if modal_token:
|
2085
|
+
print(f"ā
Modal token found (length: {len(modal_token)})")
|
2086
|
+
|
2087
|
+
# Use the Modal CLI to set the token
|
2079
2088
|
try:
|
2080
|
-
|
2081
|
-
|
2082
|
-
|
2083
|
-
|
2084
|
-
|
2089
|
+
import subprocess
|
2090
|
+
result = subprocess.run(
|
2091
|
+
["modal", "token", "set", modal_token],
|
2092
|
+
capture_output=True, text=True
|
2093
|
+
)
|
2094
|
+
if result.returncode == 0:
|
2095
|
+
print("ā
Modal token set via CLI")
|
2096
|
+
else:
|
2097
|
+
print(f"ā ļø Modal CLI token set returned: {result.stderr}")
|
2085
2098
|
except Exception as e:
|
2086
|
-
print(f"ā ļø Error
|
2087
|
-
|
2088
|
-
# If still no token, try to get workspace name as authentication test
|
2089
|
-
if not modal_token:
|
2090
|
-
# This will raise an exception if not authenticated
|
2091
|
-
workspace = modal.config.get_current_workspace_name()
|
2092
|
-
print(f"ā
Modal authentication verified (workspace: {workspace})")
|
2099
|
+
print(f"ā ļø Error setting token via CLI: {e}")
|
2093
2100
|
else:
|
2094
|
-
print(
|
2095
|
-
|
2096
|
-
|
2097
|
-
|
2098
|
-
|
2099
|
-
|
2100
|
-
|
2101
|
-
print("="*80)
|
2102
|
-
print("GitArsenal requires Modal authentication to create cloud environments.")
|
2103
|
-
|
2104
|
-
# Check if token is in environment
|
2105
|
-
modal_token = os.environ.get("MODAL_TOKEN_ID")
|
2101
|
+
print("ā No Modal token found in environment variables")
|
2102
|
+
return None
|
2103
|
+
|
2104
|
+
except Exception as e:
|
2105
|
+
print(f"ā ļø Error checking Modal token: {e}")
|
2106
|
+
# Try to use the token from environment
|
2107
|
+
modal_token = os.environ.get("MODAL_TOKEN_ID") or os.environ.get("MODAL_TOKEN")
|
2106
2108
|
if not modal_token:
|
2107
|
-
print("
|
2109
|
+
print("ā No Modal token available. Cannot proceed.")
|
2108
2110
|
return None
|
2109
|
-
|
2110
|
-
|
2111
|
-
|
2112
|
-
|
2113
|
-
|
2114
|
-
modal.config._auth_config.token_id = modal_token
|
2115
|
-
print("ā
Modal token set in config")
|
2116
|
-
except Exception as e:
|
2117
|
-
print(f"ā ļø Error setting Modal token: {e}")
|
2118
|
-
return None
|
2111
|
+
|
2112
|
+
print(f"š Using Modal token from environment (length: {len(modal_token)})")
|
2113
|
+
# Set it in both environment variables
|
2114
|
+
os.environ["MODAL_TOKEN_ID"] = modal_token
|
2115
|
+
os.environ["MODAL_TOKEN"] = modal_token
|
2119
2116
|
except Exception as e:
|
2120
2117
|
print(f"ā ļø Error checking Modal authentication: {e}")
|
2121
2118
|
print("Continuing anyway, but Modal operations may fail")
|
@@ -2176,83 +2173,59 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
2176
2173
|
print(f" - MODAL_TOKEN_ID in env: {'Yes' if modal_token else 'No'}")
|
2177
2174
|
print(f" - Token length: {len(modal_token) if modal_token else 'N/A'}")
|
2178
2175
|
|
2176
|
+
# Verify we can create a Modal app
|
2179
2177
|
try:
|
2180
|
-
|
2181
|
-
|
2182
|
-
|
2183
|
-
print(f" - Error getting workspace: {e}")
|
2184
|
-
|
2185
|
-
# Verify token is valid by trying a simple Modal operation
|
2186
|
-
try:
|
2187
|
-
print("š Verifying Modal token validity...")
|
2188
|
-
# Try to access Modal API
|
2189
|
-
if not modal_token:
|
2190
|
-
print("ā No Modal token available. Cannot proceed.")
|
2191
|
-
return None
|
2192
|
-
|
2193
|
-
# Set token directly in modal config again to be sure
|
2194
|
-
modal.config._auth_config.token_id = modal_token
|
2195
|
-
|
2196
|
-
# Try to list volumes as a simple API test
|
2197
|
-
try:
|
2198
|
-
test_vol = modal.Volume.list()
|
2199
|
-
print(f"ā
Modal token is valid! Successfully listed volumes.")
|
2200
|
-
except Exception as vol_err:
|
2201
|
-
print(f"ā Token validation failed: {vol_err}")
|
2202
|
-
print("Please check that your Modal token is valid and properly set.")
|
2203
|
-
return None
|
2178
|
+
print("š Testing Modal app creation...")
|
2179
|
+
app = modal.App(app_name)
|
2180
|
+
print("ā
Created Modal app successfully")
|
2204
2181
|
except Exception as e:
|
2205
|
-
print(f"ā Error
|
2182
|
+
print(f"ā Error creating Modal app: {e}")
|
2206
2183
|
return None
|
2207
2184
|
|
2208
2185
|
# Create SSH-enabled image
|
2209
|
-
ssh_image = (
|
2210
|
-
modal.Image.debian_slim()
|
2211
|
-
.apt_install(
|
2212
|
-
"openssh-server", "sudo", "curl", "wget", "vim", "htop", "git",
|
2213
|
-
"python3", "python3-pip", "build-essential", "tmux", "screen", "nano",
|
2214
|
-
"gpg", "ca-certificates", "software-properties-common"
|
2215
|
-
)
|
2216
|
-
.pip_install("uv", "modal") # Fast Python package installer and Modal
|
2217
|
-
.run_commands(
|
2218
|
-
# Create SSH directory
|
2219
|
-
"mkdir -p /var/run/sshd",
|
2220
|
-
"mkdir -p /root/.ssh",
|
2221
|
-
"chmod 700 /root/.ssh",
|
2222
|
-
|
2223
|
-
# Configure SSH server
|
2224
|
-
"sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config",
|
2225
|
-
"sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
|
2226
|
-
"sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config",
|
2227
|
-
|
2228
|
-
# SSH keep-alive settings
|
2229
|
-
"echo 'ClientAliveInterval 60' >> /etc/ssh/sshd_config",
|
2230
|
-
"echo 'ClientAliveCountMax 3' >> /etc/ssh/sshd_config",
|
2231
|
-
|
2232
|
-
# Generate SSH host keys
|
2233
|
-
"ssh-keygen -A",
|
2234
|
-
|
2235
|
-
# Install Modal CLI
|
2236
|
-
"pip install modal",
|
2237
|
-
|
2238
|
-
# Set up a nice bash prompt
|
2239
|
-
"echo 'export PS1=\"\\[\\e[1;32m\\]modal:\\[\\e[1;34m\\]\\w\\[\\e[0m\\]$ \"' >> /root/.bashrc",
|
2240
|
-
)
|
2241
|
-
)
|
2242
|
-
|
2243
|
-
# Create the Modal app
|
2244
2186
|
try:
|
2245
|
-
|
2246
|
-
|
2187
|
+
print("š¦ Building SSH-enabled image...")
|
2188
|
+
ssh_image = (
|
2189
|
+
modal.Image.debian_slim()
|
2190
|
+
.apt_install(
|
2191
|
+
"openssh-server", "sudo", "curl", "wget", "vim", "htop", "git",
|
2192
|
+
"python3", "python3-pip", "build-essential", "tmux", "screen", "nano",
|
2193
|
+
"gpg", "ca-certificates", "software-properties-common"
|
2194
|
+
)
|
2195
|
+
.pip_install("uv", "modal") # Fast Python package installer and Modal
|
2196
|
+
.run_commands(
|
2197
|
+
# Create SSH directory
|
2198
|
+
"mkdir -p /var/run/sshd",
|
2199
|
+
"mkdir -p /root/.ssh",
|
2200
|
+
"chmod 700 /root/.ssh",
|
2201
|
+
|
2202
|
+
# Configure SSH server
|
2203
|
+
"sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config",
|
2204
|
+
"sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
|
2205
|
+
"sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config",
|
2206
|
+
|
2207
|
+
# SSH keep-alive settings
|
2208
|
+
"echo 'ClientAliveInterval 60' >> /etc/ssh/sshd_config",
|
2209
|
+
"echo 'ClientAliveCountMax 3' >> /etc/ssh/sshd_config",
|
2210
|
+
|
2211
|
+
# Generate SSH host keys
|
2212
|
+
"ssh-keygen -A",
|
2213
|
+
|
2214
|
+
# Set up a nice bash prompt
|
2215
|
+
"echo 'export PS1=\"\\[\\e[1;32m\\]modal:\\[\\e[1;34m\\]\\w\\[\\e[0m\\]$ \"' >> /root/.bashrc",
|
2216
|
+
)
|
2217
|
+
)
|
2218
|
+
print("ā
SSH image built successfully")
|
2247
2219
|
except Exception as e:
|
2248
|
-
print(f"ā Error
|
2220
|
+
print(f"ā Error building SSH image: {e}")
|
2249
2221
|
return None
|
2250
|
-
|
2222
|
+
|
2251
2223
|
# Configure volumes if available
|
2252
2224
|
volumes_config = {}
|
2253
2225
|
if volume:
|
2254
2226
|
volumes_config[volume_mount_path] = volume
|
2255
|
-
|
2227
|
+
|
2228
|
+
# Define the SSH container function
|
2256
2229
|
@app.function(
|
2257
2230
|
image=ssh_image,
|
2258
2231
|
timeout=timeout_minutes * 60, # Convert to seconds
|
@@ -2262,27 +2235,17 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
2262
2235
|
serialized=True,
|
2263
2236
|
volumes=volumes_config if volumes_config else None,
|
2264
2237
|
)
|
2265
|
-
def
|
2238
|
+
def ssh_container_function():
|
2266
2239
|
"""Start SSH container with password authentication and optional setup."""
|
2267
2240
|
import subprocess
|
2268
2241
|
import time
|
2269
2242
|
import os
|
2270
2243
|
|
2271
2244
|
# Set root password
|
2272
|
-
|
2273
|
-
subprocess.run(
|
2274
|
-
["bash", "-c", f"echo 'root:{ssh_password}' | chpasswd"],
|
2275
|
-
check=True
|
2276
|
-
)
|
2277
|
-
|
2278
|
-
print("ā
Root password configured")
|
2279
|
-
|
2280
|
-
# Start SSH daemon in background
|
2281
|
-
print("š Starting SSH daemon...")
|
2282
|
-
ssh_process = subprocess.Popen(["/usr/sbin/sshd", "-D"])
|
2245
|
+
subprocess.run(["bash", "-c", f"echo 'root:{ssh_password}' | chpasswd"], check=True)
|
2283
2246
|
|
2284
|
-
#
|
2285
|
-
|
2247
|
+
# Start SSH service
|
2248
|
+
subprocess.run(["service", "ssh", "start"], check=True)
|
2286
2249
|
|
2287
2250
|
# Clone repository if provided
|
2288
2251
|
if repo_url:
|
@@ -2290,18 +2253,14 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
2290
2253
|
print(f"š„ Cloning repository: {repo_url}")
|
2291
2254
|
|
2292
2255
|
try:
|
2293
|
-
subprocess.run(["git", "clone", repo_url, "/root
|
2256
|
+
subprocess.run(["git", "clone", repo_url], check=True, cwd="/root")
|
2294
2257
|
print(f"ā
Repository cloned successfully: {repo_name_from_url}")
|
2295
2258
|
|
2296
2259
|
# Change to repository directory
|
2297
|
-
repo_dir = f"/root/
|
2298
|
-
os.
|
2299
|
-
|
2300
|
-
|
2301
|
-
# Initialize git submodules if they exist
|
2302
|
-
if os.path.exists(".gitmodules"):
|
2303
|
-
print("š¦ Initializing git submodules...")
|
2304
|
-
subprocess.run(["git", "submodule", "update", "--init", "--recursive"], check=True)
|
2260
|
+
repo_dir = f"/root/{repo_name_from_url}"
|
2261
|
+
if os.path.exists(repo_dir):
|
2262
|
+
os.chdir(repo_dir)
|
2263
|
+
print(f"š Changed to repository directory: {repo_dir}")
|
2305
2264
|
|
2306
2265
|
except subprocess.CalledProcessError as e:
|
2307
2266
|
print(f"ā Failed to clone repository: {e}")
|
@@ -2312,16 +2271,8 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
2312
2271
|
for i, cmd in enumerate(setup_commands, 1):
|
2313
2272
|
print(f"š Executing command {i}/{len(setup_commands)}: {cmd}")
|
2314
2273
|
try:
|
2315
|
-
|
2316
|
-
|
2317
|
-
result = subprocess.run(
|
2318
|
-
cmd,
|
2319
|
-
shell=True,
|
2320
|
-
check=True,
|
2321
|
-
cwd=work_dir,
|
2322
|
-
capture_output=True,
|
2323
|
-
text=True
|
2324
|
-
)
|
2274
|
+
result = subprocess.run(cmd, shell=True, check=True,
|
2275
|
+
capture_output=True, text=True)
|
2325
2276
|
if result.stdout:
|
2326
2277
|
print(f"ā
Output: {result.stdout}")
|
2327
2278
|
except subprocess.CalledProcessError as e:
|
@@ -2329,11 +2280,9 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
2329
2280
|
if e.stderr:
|
2330
2281
|
print(f"ā Error: {e.stderr}")
|
2331
2282
|
|
2332
|
-
# Create
|
2333
|
-
|
2334
|
-
|
2335
|
-
# Print connection information
|
2336
|
-
host, port = tunnel.tcp_socket
|
2283
|
+
# Create SSH tunnel
|
2284
|
+
with modal.forward(22) as tunnel:
|
2285
|
+
host, port = tunnel.host, tunnel.port
|
2337
2286
|
|
2338
2287
|
print("\n" + "=" * 80)
|
2339
2288
|
print("š SSH CONTAINER IS READY!")
|
@@ -2345,80 +2294,33 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
2345
2294
|
print()
|
2346
2295
|
print("š CONNECT USING THIS COMMAND:")
|
2347
2296
|
print(f"ssh -p {port} root@{host}")
|
2348
|
-
print()
|
2349
|
-
print("š” Connection Tips:")
|
2350
|
-
print("⢠Copy the password above and paste when prompted")
|
2351
|
-
print("⢠Use Ctrl+C to disconnect (container will keep running)")
|
2352
|
-
print("⢠Type 'exit' in the SSH session to close the connection")
|
2353
|
-
if repo_url:
|
2354
|
-
print("⢠Your repository is in: /root/repo")
|
2355
|
-
if volume:
|
2356
|
-
print(f"⢠Persistent storage is mounted at: {volume_mount_path}")
|
2357
|
-
print(f"⢠Container will auto-terminate in {timeout_minutes} minutes")
|
2358
|
-
print()
|
2359
|
-
print("š ļø Available Tools:")
|
2360
|
-
print("⢠Python 3 with pip and uv package manager")
|
2361
|
-
print("⢠Git, curl, wget, vim, nano, htop")
|
2362
|
-
print("⢠tmux and screen for session management")
|
2363
|
-
print(f"⢠{gpu_type} GPU access")
|
2364
2297
|
print("=" * 80)
|
2365
2298
|
|
2366
|
-
# Keep the container running
|
2367
|
-
|
2368
|
-
|
2369
|
-
|
2370
|
-
|
2371
|
-
|
2372
|
-
|
2373
|
-
|
2374
|
-
|
2375
|
-
|
2376
|
-
if ssh_process.poll() is not None:
|
2377
|
-
print("ā ļø SSH daemon stopped unexpectedly, restarting...")
|
2378
|
-
ssh_process = subprocess.Popen(["/usr/sbin/sshd", "-D"])
|
2379
|
-
time.sleep(2)
|
2380
|
-
|
2381
|
-
# Print alive status every 5 minutes
|
2382
|
-
elapsed_minutes = (current_time - start_time) / 60
|
2383
|
-
if int(elapsed_minutes) % 5 == 0 and elapsed_minutes > 0:
|
2384
|
-
remaining_minutes = timeout_minutes - elapsed_minutes
|
2385
|
-
print(f"ā±ļø Container alive for {elapsed_minutes:.0f} minutes, {remaining_minutes:.0f} minutes remaining")
|
2386
|
-
|
2387
|
-
last_check = current_time
|
2388
|
-
|
2389
|
-
time.sleep(10)
|
2390
|
-
|
2391
|
-
except KeyboardInterrupt:
|
2392
|
-
print("\nš Received shutdown signal...")
|
2393
|
-
print("š Stopping SSH daemon...")
|
2394
|
-
ssh_process.terminate()
|
2395
|
-
ssh_process.wait()
|
2396
|
-
print("ā
Container shutting down gracefully")
|
2397
|
-
return
|
2299
|
+
# Keep the container running
|
2300
|
+
while True:
|
2301
|
+
time.sleep(30)
|
2302
|
+
# Check if SSH service is still running
|
2303
|
+
try:
|
2304
|
+
subprocess.run(["service", "ssh", "status"], check=True,
|
2305
|
+
capture_output=True)
|
2306
|
+
except subprocess.CalledProcessError:
|
2307
|
+
print("ā ļø SSH service stopped, restarting...")
|
2308
|
+
subprocess.run(["service", "ssh", "start"], check=True)
|
2398
2309
|
|
2399
2310
|
# Run the container
|
2400
|
-
print("ā³ Starting container... This may take 1-2 minutes...")
|
2401
|
-
print("š¦ Building image and allocating resources...")
|
2402
|
-
|
2403
2311
|
try:
|
2404
|
-
|
2312
|
+
print("ā³ Starting container... This may take 1-2 minutes...")
|
2313
|
+
|
2314
|
+
# Start the container in a new thread to avoid blocking
|
2405
2315
|
with modal.enable_output():
|
2406
2316
|
with app.run():
|
2407
|
-
|
2317
|
+
ssh_container_function.remote()
|
2408
2318
|
|
2409
2319
|
return {
|
2410
2320
|
"app_name": app_name,
|
2411
2321
|
"ssh_password": ssh_password,
|
2412
|
-
"volume_name": volume_name
|
2413
|
-
"volume_mount_path": volume_mount_path if volume else None
|
2322
|
+
"volume_name": volume_name
|
2414
2323
|
}
|
2415
|
-
except modal.exception.AuthError as auth_err:
|
2416
|
-
print(f"ā Modal authentication error: {auth_err}")
|
2417
|
-
print("š Please check that your Modal token is valid and properly set")
|
2418
|
-
return None
|
2419
|
-
except KeyboardInterrupt:
|
2420
|
-
print("\nš Container startup interrupted")
|
2421
|
-
return None
|
2422
2324
|
except Exception as e:
|
2423
2325
|
print(f"ā Error running container: {e}")
|
2424
2326
|
return None
|