Skip to content

Commit 758f2bd

Browse files
Merge pull request #286 from Ratio1/develop
Develop
2 parents 5e6bbe7 + 4ad5b34 commit 758f2bd

File tree

4 files changed

+136
-41
lines changed

4 files changed

+136
-41
lines changed

extensions/business/chain_dist/chain_dist_monitor.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ def check_all_jobs(self):
100100
pass
101101
else:
102102
known_apps = self.netmon.network_known_apps()
103+
first_closable_job_id = self.bc.get_first_closable_job_id()
103104
for job_id in unvalidated_job_ids:
104105
if not job_id:
105106
continue
@@ -119,7 +120,7 @@ def check_all_jobs(self):
119120
#endfor
120121

121122
# if we have running nodes, submit the update
122-
is_job_to_be_closed = self.bc.get_first_closable_job_id() == job_id
123+
is_job_to_be_closed = first_closable_job_id == job_id
123124
if ((not is_job_to_be_closed) and len(running_nodes) > 0) or (is_job_to_be_closed and (len(running_nodes) == 0)):
124125
running_nodes_eth = [self.bc.node_address_to_eth_address(node) for node in running_nodes]
125126
running_nodes_eth = sorted(running_nodes_eth)

extensions/business/container_apps/container_utils.py

Lines changed: 132 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,17 @@ def _allocate_port(self, required_port=0, allow_dynamic=False, sleep_time=5):
229229

230230
def _setup_resource_limits_and_ports(self):
231231
"""
232-
Sets up resource limits for the container based on the configuration.
232+
Sets up resource limits and port mappings for the container based on configuration.
233+
234+
Port Handling Logic:
235+
1. Process all ports from CONTAINER_RESOURCES["ports"] first
236+
2. If main PORT exists and not in ports mapping, allocate it
237+
3. All ports (including main PORT) go into extra_ports_mapping
238+
4. Validate no duplicate container ports
239+
240+
Priority:
241+
- Explicit mappings in CONTAINER_RESOURCES["ports"] take precedence
242+
- Main PORT is allocated dynamically if not explicitly mapped
233243
"""
234244
DEFAULT_CPU_LIMIT = 1
235245
DEFAULT_GPU_LIMIT = 0
@@ -244,53 +254,122 @@ def _setup_resource_limits_and_ports(self):
244254

245255
ports = container_resources.get("ports", DEFAULT_PORTS)
246256

257+
# Track which container ports have been mapped to avoid duplicates
258+
mapped_container_ports = set()
259+
main_port_mapped = False
260+
247261
if len(ports) > 0:
248262
if isinstance(ports, list):
249-
# Handle list of container ports
263+
# Handle list of container ports - allocate dynamic host ports
264+
self.P("Processing container ports list...")
250265
for container_port in ports:
251-
self.P(f"Additional container port {container_port} specified. Finding available host port ...")
266+
if container_port in mapped_container_ports:
267+
self.P(f"Warning: Container port {container_port} already mapped, skipping duplicate", color='y')
268+
continue
269+
270+
self.P(f"Container port {container_port} specified. Finding available host port...")
252271
host_port = self._allocate_port()
253272
self.extra_ports_mapping[host_port] = container_port
254-
self.P(f"Allocated free host port {host_port} for container port {container_port}.")
255-
else:
256-
# Handle dict of port mappings
257-
# Check if main app port is mapped to a specific host port
258-
if self.cfg_port and isinstance(ports, dict) and self.cfg_port in ports.values():
259-
container_port = self.cfg_port
260-
requested_host_port = int(next((k for k, v in ports.items() if v == container_port), 0))
261-
262-
self.P(f"Main app port {self.cfg_port} is not mapped to any host port in the ports dict. Allocating a new host port ...")
263-
264-
self.port = self._allocate_port(requested_host_port, allow_dynamic=True)
265-
266-
if self.port != requested_host_port:
267-
self.P(f"Requested host port {requested_host_port} is not available. Allocated port {self.port} instead.")
268-
269-
self.extra_ports_mapping[self.port] = container_port
270-
273+
mapped_container_ports.add(container_port)
274+
self.P(f"Allocated host port {host_port} -> container port {container_port}")
275+
276+
# Check if this is the main port
277+
if self.cfg_port and container_port == self.cfg_port:
278+
self.port = host_port
279+
main_port_mapped = True
280+
self.P(f"Main PORT {self.cfg_port} mapped to host port {host_port}", color='g')
281+
282+
elif isinstance(ports, dict):
283+
# Handle dict of explicit host_port -> container_port mappings
284+
self.P("Processing explicit port mappings...")
285+
286+
# First, validate for duplicate container ports
287+
container_ports_in_dict = list(ports.values())
288+
if len(container_ports_in_dict) != len(set(container_ports_in_dict)):
289+
raise ValueError(
290+
f"Duplicate container ports found in CONTAINER_RESOURCES['ports']: {ports}. "
291+
"Each container port can only be mapped once."
292+
)
293+
294+
# Process all explicit mappings
271295
for host_port, container_port in ports.items():
272296
try:
273297
host_port = int(host_port)
298+
container_port = int(container_port)
299+
300+
# Check if this mapping was already processed
274301
if host_port in self.extra_ports_mapping:
275-
self.Pd(f"Host port {host_port} is already allocated for container port {self.extra_ports_mapping[host_port]}. Skipping allocation.")
276-
continue
277-
self._allocate_port(host_port)
302+
existing_container_port = self.extra_ports_mapping[host_port]
303+
if existing_container_port == container_port:
304+
self.Pd(f"Port mapping {host_port}->{container_port} already exists, skipping")
305+
continue
306+
else:
307+
raise ValueError(
308+
f"Host port {host_port} is already mapped to container port {existing_container_port}. "
309+
f"Cannot map it to {container_port}"
310+
)
311+
312+
# Allocate the requested host port
313+
self.P(f"Allocating requested host port {host_port} for container port {container_port}...")
314+
allocated_port = self._allocate_port(host_port, allow_dynamic=False)
315+
316+
if allocated_port != host_port:
317+
raise RuntimeError(
318+
f"Failed to allocate requested host port {host_port}. "
319+
f"Port may be in use by another process."
320+
)
321+
278322
self.extra_ports_mapping[host_port] = container_port
323+
mapped_container_ports.add(container_port)
324+
self.P(f"Allocated host port {host_port} -> container port {container_port}", color='g')
325+
326+
# Check if this is the main port
327+
if self.cfg_port and container_port == self.cfg_port:
328+
self.port = host_port
329+
main_port_mapped = True
330+
self.P(f"Main PORT {self.cfg_port} mapped to host port {host_port} (from explicit mapping)", color='g')
331+
332+
except ValueError as e:
333+
raise ValueError(f"Invalid port mapping {host_port}:{container_port} - {e}")
279334
except Exception as e:
280-
self.P(f"Port {host_port} is not available.")
281-
self.P(e)
282-
raise RuntimeError(f"Port {host_port} is not available.")
283-
# endfor each port
284-
# endif ports list or dict
285-
# endif ports
335+
self.P(f"Failed to allocate port {host_port}: {e}", color='r')
336+
raise RuntimeError(f"Port allocation failed for {host_port}:{container_port}")
337+
else:
338+
self.P(f"Invalid ports configuration type: {type(ports)}. Expected list or dict.", color='r')
339+
340+
# Handle main PORT if it exists and wasn't mapped yet
341+
if self.cfg_port and not main_port_mapped:
342+
if self.cfg_port in mapped_container_ports:
343+
# Main PORT was mapped to a different host port in the loop above
344+
# Find which host port it was mapped to
345+
for h_port, c_port in self.extra_ports_mapping.items():
346+
if c_port == self.cfg_port:
347+
self.port = h_port
348+
self.P(f"Main PORT {self.cfg_port} already mapped to host port {h_port}", color='d')
349+
break
350+
else:
351+
# Allocate a dynamic host port for the main PORT
352+
self.P(f"Main PORT {self.cfg_port} not in explicit mappings. Allocating dynamic host port...")
353+
self.port = self._allocate_port(allow_dynamic=True)
354+
self.extra_ports_mapping[self.port] = self.cfg_port
355+
mapped_container_ports.add(self.cfg_port)
356+
self.P(f"Allocated host port {self.port} -> main PORT {self.cfg_port}", color='g')
357+
# endif main PORT
358+
# endif main_port_mapped
286359
else:
360+
# No container resources specified, use defaults
287361
self._cpu_limit = DEFAULT_CPU_LIMIT
288362
self._gpu_limit = DEFAULT_GPU_LIMIT
289363
self._mem_limit = DEFAULT_MEM_LIMIT
290-
# endif resource limits
291364

292-
if not self.port and self.cfg_port:
293-
self.port = self._allocate_port(allow_dynamic=True) # Allocate a port for the container if needed
365+
# Still handle main PORT if specified
366+
if self.cfg_port:
367+
self.P(f"No CONTAINER_RESOURCES specified. Allocating dynamic host port for main PORT {self.cfg_port}...")
368+
self.port = self._allocate_port(allow_dynamic=True)
369+
self.extra_ports_mapping[self.port] = self.cfg_port
370+
self.P(f"Allocated host port {self.port} -> main PORT {self.cfg_port}", color='g')
371+
# endif main PORT
372+
# endif container_resources
294373
return
295374

296375
def _set_directory_permissions(self, path, mode=0o777):
@@ -469,6 +548,12 @@ def _configure_file_volumes(self):
469548

470549
### COMMON CONTAINER UTILITY METHODS ###
471550
def _setup_env_and_ports(self):
551+
"""
552+
Sets up environment variables and formats port mappings for Docker.
553+
554+
This method should NOT allocate ports - only format already-allocated ports.
555+
All port allocations happen in _setup_resource_limits_and_ports.
556+
"""
472557
# Environment variables
473558
# allow cfg_env to override default env vars
474559
self.env = self._get_default_env_vars()
@@ -479,12 +564,21 @@ def _setup_env_and_ports(self):
479564
self.env.update(self.dynamic_env)
480565
# endif dynamic env
481566

482-
# Ports mapping
483-
ports_mapping = self.extra_ports_mapping.copy() if self.extra_ports_mapping else {}
484-
if self.cfg_port and self.port:
485-
ports_mapping[self.port] = self.cfg_port
486-
# end if main port
487-
self.inverted_ports_mapping = {f"{v}/tcp": str(k) for k, v in ports_mapping.items()}
567+
# Format ports for Docker API
568+
# Docker expects: {"container_port/tcp": "host_port"}
569+
# extra_ports_mapping contains: {host_port: container_port}
570+
# All ports (including main PORT) are already in extra_ports_mapping
571+
self.inverted_ports_mapping = {
572+
f"{container_port}/tcp": str(host_port)
573+
for host_port, container_port in self.extra_ports_mapping.items()
574+
}
575+
576+
# Log the final port mapping
577+
if self.inverted_ports_mapping:
578+
self.P("Final port mappings:", color='b')
579+
for container_port, host_port in self.inverted_ports_mapping.items():
580+
is_main = "(main)" if self.cfg_port and str(self.cfg_port) in container_port else ""
581+
self.P(f" Container {container_port} -> Host {host_port} {is_main}", color='d')
488582

489583
return
490584

extensions/business/deeploy/deeploy_mixin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -917,7 +917,7 @@ def deeploy_check_payment_and_job_owner(self, inputs, sender, is_create, debug=F
917917
Check if the payment is valid for the given job.
918918
"""
919919
allow_unpaid = inputs.get("allow_unpaid_job", False)
920-
if allow_unpaid:
920+
if allow_unpaid and self.bc.get_evm_network() == 'devnet':
921921
return True
922922
job_id = inputs.get(DEEPLOY_KEYS.JOB_ID, None)
923923
self.Pd(f"Checking payment for job {job_id} by sender {sender}{' (debug mode)' if debug else ''}")

ver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__VER__ = '2.9.800'
1+
__VER__ = '2.9.810'

0 commit comments

Comments
 (0)