Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions cloudproxy/providers/aws/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ def aws_check_alive(instance_config=None):
"default"
)

ip_ready = []
pending_ips = []
ip_ready = set()
pending_ips = set()
instances_to_recycle = []

# First pass: identify healthy and pending instances
Expand All @@ -78,10 +78,7 @@ def aws_check_alive(instance_config=None):
datetime.timezone.utc
) - instance["Instances"][0]["LaunchTime"]

if config["age_limit"] > 0 and elapsed > datetime.timedelta(seconds=config["age_limit"]):
# Queue for potential recycling
instances_to_recycle.append((instance, elapsed))
elif instance["Instances"][0]["State"]["Name"] == "stopped":
if instance["Instances"][0]["State"]["Name"] == "stopped":
logger.info(
f"Waking up: AWS {instance_config.get('display_name', 'default')} -> Instance " + instance["Instances"][0]["InstanceId"]
)
Expand All @@ -99,13 +96,16 @@ def aws_check_alive(instance_config=None):
f"Pending: AWS {instance_config.get('display_name', 'default')} -> " + instance["Instances"][0]["PublicIpAddress"]
)
if "PublicIpAddress" in instance["Instances"][0]:
pending_ips.append(instance["Instances"][0]["PublicIpAddress"])
pending_ips.add(instance["Instances"][0]["PublicIpAddress"])
# Must be "running" if none of the above, check if alive or not.
elif check_alive(instance["Instances"][0]["PublicIpAddress"]):
logger.info(
f"Alive: AWS {instance_config.get('display_name', 'default')} -> " + instance["Instances"][0]["PublicIpAddress"]
)
ip_ready.append(instance["Instances"][0]["PublicIpAddress"])
ip_ready.add(instance["Instances"][0]["PublicIpAddress"])
if config["age_limit"] > 0 and elapsed > datetime.timedelta(seconds=config["age_limit"]):
# Queue for potential recycling
instances_to_recycle.append((instance, elapsed))
else:
if elapsed > datetime.timedelta(minutes=10):
delete_proxy(instance["Instances"][0]["InstanceId"], instance_config)
Expand All @@ -118,7 +118,7 @@ def aws_check_alive(instance_config=None):
f"Waiting: AWS {instance_config.get('display_name', 'default')} -> " + instance["Instances"][0]["PublicIpAddress"]
)
if "PublicIpAddress" in instance["Instances"][0]:
pending_ips.append(instance["Instances"][0]["PublicIpAddress"])
pending_ips.add(instance["Instances"][0]["PublicIpAddress"])
except (TypeError, KeyError):
logger.info(f"Pending: AWS {instance_config.get('display_name', 'default')} -> allocating ip")

Expand Down Expand Up @@ -147,6 +147,7 @@ def aws_check_alive(instance_config=None):
# Mark as recycling and delete
rolling_manager.mark_proxy_recycling("aws", instance_name, instance_ip)
delete_proxy(inst["Instances"][0]["InstanceId"], instance_config)
ip_ready.discard(instance_ip)
rolling_manager.mark_proxy_recycled("aws", instance_name, instance_ip)
logger.info(
f"Rolling deployment: Recycled AWS {instance_config.get('display_name', 'default')} instance (age limit) -> {instance_ip}"
Expand All @@ -160,6 +161,7 @@ def aws_check_alive(instance_config=None):
for inst, elapsed in instances_to_recycle:
delete_proxy(inst["Instances"][0]["InstanceId"], instance_config)
if "PublicIpAddress" in inst["Instances"][0]:
ip_ready.discard(inst["Instances"][0]["PublicIpAddress"])
logger.info(
f"Recycling AWS {instance_config.get('display_name', 'default')} instance, reached age limit -> " + inst["Instances"][0]["PublicIpAddress"]
)
Expand All @@ -168,7 +170,7 @@ def aws_check_alive(instance_config=None):
f"Recycling AWS {instance_config.get('display_name', 'default')} instance, reached age limit -> " + inst["Instances"][0]["InstanceId"]
)

return ip_ready
return list(ip_ready)


def aws_check_delete(instance_config=None):
Expand Down
24 changes: 13 additions & 11 deletions cloudproxy/providers/digitalocean/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ def do_check_alive(instance_config=None):
"default"
)

ip_ready = []
pending_ips = []
ip_ready = set()
pending_ips = set()
droplets_to_recycle = []

# First pass: identify healthy and pending droplets
Expand All @@ -81,18 +81,18 @@ def do_check_alive(instance_config=None):
if created_at is None:
# If parsing fails but doesn't raise an exception, log and continue
logger.info(f"Pending: DO {display_name} allocating (invalid timestamp)")
pending_ips.append(str(droplet.ip_address))
pending_ips.add(str(droplet.ip_address))
continue

# Calculate elapsed time
elapsed = datetime.datetime.now(datetime.timezone.utc) - created_at

# Check if the droplet has reached the age limit
if config["age_limit"] > 0 and elapsed > datetime.timedelta(seconds=config["age_limit"]):
droplets_to_recycle.append((droplet, elapsed))
elif check_alive(droplet.ip_address):
if check_alive(droplet.ip_address):
logger.info(f"Alive: DO {display_name} -> {str(droplet.ip_address)}")
ip_ready.append(droplet.ip_address)
ip_ready.add(droplet.ip_address)
# Check if the droplet has reached the age limit
if config["age_limit"] > 0 and elapsed > datetime.timedelta(seconds=config["age_limit"]):
droplets_to_recycle.append((droplet, elapsed))
else:
# Check if the droplet has been pending for too long
if elapsed > datetime.timedelta(minutes=10):
Expand All @@ -102,12 +102,12 @@ def do_check_alive(instance_config=None):
)
else:
logger.info(f"Waiting: DO {display_name} -> {str(droplet.ip_address)}")
pending_ips.append(str(droplet.ip_address))
pending_ips.add(str(droplet.ip_address))
except TypeError:
# This happens when dateparser.parse raises a TypeError
logger.info(f"Pending: DO {display_name} allocating")
if hasattr(droplet, 'ip_address'):
pending_ips.append(str(droplet.ip_address))
pending_ips.add(str(droplet.ip_address))

# Update rolling manager with current proxy health status
rolling_manager.update_proxy_health("digitalocean", instance_name, ip_ready, pending_ips)
Expand All @@ -133,6 +133,7 @@ def do_check_alive(instance_config=None):
# Mark as recycling and delete
rolling_manager.mark_proxy_recycling("digitalocean", instance_name, droplet_ip)
delete_proxy(droplet, instance_config)
ip_ready.discard(droplet.ip_address)
rolling_manager.mark_proxy_recycled("digitalocean", instance_name, droplet_ip)
logger.info(
f"Rolling deployment: Recycled DO {display_name} droplet (age limit) -> {droplet_ip}"
Expand All @@ -145,11 +146,12 @@ def do_check_alive(instance_config=None):
# Standard non-rolling recycling
for droplet, elapsed in droplets_to_recycle:
delete_proxy(droplet, instance_config)
ip_ready.discard(droplet.ip_address)
logger.info(
f"Recycling DO {display_name} droplet, reached age limit -> {str(droplet.ip_address)}"
)

return ip_ready
return list(ip_ready)


def do_check_delete(instance_config=None):
Expand Down
23 changes: 12 additions & 11 deletions cloudproxy/providers/gcp/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ def gcp_check_alive(instance_config=None):
"default"
)

ip_ready = []
pending_ips = []
ip_ready = set()
pending_ips = set()
instances_to_recycle = []

for instance in list_instances(instance_config):
Expand All @@ -72,11 +72,7 @@ def gcp_check_alive(instance_config=None):
datetime.timezone.utc
) - datetime.datetime.strptime(instance["creationTimestamp"], '%Y-%m-%dT%H:%M:%S.%f%z')

if config["age_limit"] > 0 and elapsed > datetime.timedelta(seconds=config["age_limit"]):
# Queue for potential recycling
instances_to_recycle.append((instance, elapsed))

elif instance['status'] == "TERMINATED":
if instance['status'] == "TERMINATED":
logger.info("Waking up: GCP -> Instance " + instance['name'])
started = start_proxy(instance['name'], instance_config)
if not started:
Expand All @@ -92,14 +88,17 @@ def gcp_check_alive(instance_config=None):
msg = f"{instance['name']} {access_configs['natIP'] if 'natIP' in access_configs else ''}"
logger.info("Provisioning: GCP -> " + msg)
if 'natIP' in access_configs:
pending_ips.append(access_configs['natIP'])
pending_ips.add(access_configs['natIP'])

# If none of the above, check if alive or not.
elif check_alive(instance['networkInterfaces'][0]['accessConfigs'][0]['natIP']):
access_configs = instance['networkInterfaces'][0]['accessConfigs'][0]
msg = f"{instance['name']} {access_configs['natIP']}"
logger.info("Alive: GCP -> " + msg)
ip_ready.append(access_configs['natIP'])
ip_ready.add(access_configs['natIP'])
if config["age_limit"] > 0 and elapsed > datetime.timedelta(seconds=config["age_limit"]):
# Queue for potential recycling
instances_to_recycle.append((instance, elapsed))

else:
access_configs = instance['networkInterfaces'][0]['accessConfigs'][0]
Expand All @@ -110,7 +109,7 @@ def gcp_check_alive(instance_config=None):
else:
logger.info("Waiting: GCP -> " + msg)
if 'natIP' in access_configs:
pending_ips.append(access_configs['natIP'])
pending_ips.add(access_configs['natIP'])
except (TypeError, KeyError):
logger.info("Pending: GCP -> Allocating IP")

Expand Down Expand Up @@ -140,6 +139,7 @@ def gcp_check_alive(instance_config=None):
# Mark as recycling and delete
rolling_manager.mark_proxy_recycling("gcp", instance_name, instance_ip)
delete_proxy(inst['name'], instance_config)
ip_ready.discard(instance_ip)
rolling_manager.mark_proxy_recycled("gcp", instance_name, instance_ip)
logger.info(f"Rolling deployment: Recycled GCP instance (age limit) -> {inst['name']} {instance_ip}")
else:
Expand All @@ -150,9 +150,10 @@ def gcp_check_alive(instance_config=None):
access_configs = inst['networkInterfaces'][0]['accessConfigs'][0]
msg = f"{inst['name']} {access_configs['natIP'] if 'natIP' in access_configs else ''}"
delete_proxy(inst['name'], instance_config)
ip_ready.discard(access_configs['natIP'])
logger.info("Recycling instance, reached age limit -> " + msg)

return ip_ready
return list(ip_ready)

def gcp_check_delete(instance_config=None):
"""
Expand Down
20 changes: 11 additions & 9 deletions cloudproxy/providers/hetzner/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,20 @@ def hetzner_check_alive(instance_config=None):
"default"
)

ip_ready = []
pending_ips = []
ip_ready = set()
pending_ips = set()
proxies_to_recycle = []

for proxy in list_proxies(instance_config):
elapsed = datetime.datetime.now(
datetime.timezone.utc
) - dateparser.parse(str(proxy.created))
if config["age_limit"] > 0 and elapsed > datetime.timedelta(seconds=config["age_limit"]):
# Queue for potential recycling
proxies_to_recycle.append((proxy, elapsed))
elif check_alive(proxy.public_net.ipv4.ip):
if check_alive(proxy.public_net.ipv4.ip):
logger.info(f"Alive: Hetzner {display_name} -> {str(proxy.public_net.ipv4.ip)}")
ip_ready.append(proxy.public_net.ipv4.ip)
ip_ready.add(proxy.public_net.ipv4.ip)
if config["age_limit"] > 0 and elapsed > datetime.timedelta(seconds=config["age_limit"]):
# Queue for potential recycling
proxies_to_recycle.append((proxy, elapsed))
else:
if elapsed > datetime.timedelta(minutes=10):
delete_proxy(proxy, instance_config)
Expand All @@ -88,7 +88,7 @@ def hetzner_check_alive(instance_config=None):
)
else:
logger.info(f"Waiting: Hetzner {display_name} -> {str(proxy.public_net.ipv4.ip)}")
pending_ips.append(str(proxy.public_net.ipv4.ip))
pending_ips.add(str(proxy.public_net.ipv4.ip))

# Update rolling manager with current proxy health status
rolling_manager.update_proxy_health("hetzner", instance_name, ip_ready, pending_ips)
Expand All @@ -114,6 +114,7 @@ def hetzner_check_alive(instance_config=None):
# Mark as recycling and delete
rolling_manager.mark_proxy_recycling("hetzner", instance_name, proxy_ip)
delete_proxy(prox, instance_config)
ip_ready.discard(proxy_ip)
rolling_manager.mark_proxy_recycled("hetzner", instance_name, proxy_ip)
logger.info(f"Rolling deployment: Recycled Hetzner {display_name} proxy (age limit) -> {proxy_ip}")
else:
Expand All @@ -122,9 +123,10 @@ def hetzner_check_alive(instance_config=None):
# Standard non-rolling recycling
for prox, elapsed in proxies_to_recycle:
delete_proxy(prox, instance_config)
ip_ready.discard(prox.public_net.ipv4.ip)
logger.info(f"Recycling Hetzner {display_name} proxy, reached age limit -> {str(prox.public_net.ipv4.ip)}")

return ip_ready
return list(ip_ready)


def hetzner_check_delete(instance_config=None):
Expand Down
12 changes: 6 additions & 6 deletions cloudproxy/providers/rolling.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,26 +149,26 @@ def update_proxy_health(
self,
provider: str,
instance: str,
healthy_ips: List[str],
pending_ips: List[str] = None
healthy_ips: Set[str],
pending_ips: Set[str] = None
):
"""
Update the health status of proxies for a provider instance.

Args:
provider: The cloud provider name
instance: The provider instance name
healthy_ips: List of IPs that are currently healthy
pending_ips: List of IPs that are pending (newly created)
healthy_ips: Set of IPs that are currently healthy
pending_ips: Set of IPs that are pending (newly created)
"""
state = self.get_state(provider, instance)

# Update healthy proxies
state.healthy_proxies = set(healthy_ips)
state.healthy_proxies = healthy_ips

# Update pending proxies if provided
if pending_ips is not None:
state.pending = set(pending_ips)
state.pending = pending_ips

# Clean up recycling list if proxies no longer exist
existing_ips = state.healthy_proxies | state.pending
Expand Down
26 changes: 14 additions & 12 deletions cloudproxy/providers/vultr/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ def vultr_check_alive(instance_config=None):
"default"
)

ip_ready = []
pending_ips = []
ip_ready = set()
pending_ips = set()
instances_to_recycle = []

for instance in list_instances(instance_config):
Expand All @@ -89,15 +89,15 @@ def vultr_check_alive(instance_config=None):
# Calculate elapsed time
elapsed = datetime.datetime.now(datetime.timezone.utc) - created_at

# Check if the instance has reached the age limit
if config["age_limit"] > 0 and elapsed > datetime.timedelta(
seconds=config["age_limit"]):
# Queue for potential recycling
instances_to_recycle.append((instance, elapsed))
elif instance.status == "active" and instance.ip_address and check_alive(instance.ip_address):
if instance.status == "active" and instance.ip_address and check_alive(instance.ip_address):
logger.info(
f"Alive: Vultr {display_name} -> {str(instance.ip_address)}")
ip_ready.append(instance.ip_address)
ip_ready.add(instance.ip_address)
# Check if the instance has reached the age limit
if config["age_limit"] > 0 and elapsed > datetime.timedelta(
seconds=config["age_limit"]):
# Queue for potential recycling
instances_to_recycle.append((instance, elapsed))
else:
# Check if the instance has been pending for too long
if elapsed > datetime.timedelta(minutes=10):
Expand All @@ -109,12 +109,12 @@ def vultr_check_alive(instance_config=None):
logger.info(
f"Waiting: Vultr {display_name} -> {str(instance.ip_address)}")
if instance.ip_address:
pending_ips.append(instance.ip_address)
pending_ips.add(instance.ip_address)
except TypeError:
# This happens when dateparser.parse raises a TypeError
logger.info(f"Pending: Vultr {display_name} allocating")
if hasattr(instance, 'ip_address') and instance.ip_address:
pending_ips.append(instance.ip_address)
pending_ips.add(instance.ip_address)

# Update rolling manager with current proxy health status
rolling_manager.update_proxy_health("vultr", instance_name, ip_ready, pending_ips)
Expand All @@ -141,6 +141,7 @@ def vultr_check_alive(instance_config=None):
# Mark as recycling and delete
rolling_manager.mark_proxy_recycling("vultr", instance_name, instance_ip)
delete_proxy(inst, instance_config)
ip_ready.discard(instance_ip)
rolling_manager.mark_proxy_recycled("vultr", instance_name, instance_ip)
logger.info(
f"Rolling deployment: Recycled Vultr {display_name} instance (age limit) -> {instance_ip}"
Expand All @@ -153,11 +154,12 @@ def vultr_check_alive(instance_config=None):
# Standard non-rolling recycling
for inst, elapsed in instances_to_recycle:
delete_proxy(inst, instance_config)
ip_ready.discard(inst.ip_address)
logger.info(
f"Recycling Vultr {display_name} instance, reached age limit -> {str(inst.ip_address)}"
)

return ip_ready
return list(ip_ready)


def vultr_check_delete(instance_config=None):
Expand Down
Loading