This commit is contained in:
2025-08-15 14:43:50 +09:30
parent 3aedc95f5a
commit 0952bc9a23
18 changed files with 436 additions and 363 deletions
+65 -28
View File
@@ -6,18 +6,20 @@ This module provides API routes for managing Certificate Authorities and Certifi
from flask import request, jsonify
from app import db, logger
from app.models.certificate_models import CertificateAuthority, Certificate
from app.models.certificate_models import CertificateAuthority, Certificate, CertificateRevocationList
from app.models.models import Project
from cryptography import x509
from app.utils.certificate_utils import (
generate_self_signed_ca,
issue_certificate,
generate_self_signed_ca,
issue_certificate,
revoke_certificate,
generate_crl_for_ca,
encrypt_private_key,
decrypt_private_key
)
from app.controller import api_bp
from app.utils.standard_responses import api_response
from datetime import datetime
from datetime import datetime, timedelta
import uuid
@@ -86,7 +88,8 @@ def create_ca_for_project(project_id):
)
# Create a new CA
common_name = f"rootca.{project_id}.xcloudify.tech"
domain_name = f"{project_id}.xcloudify.tech"
common_name = f"rootca.{domain_name}"
ca_data = generate_self_signed_ca(
common_name=common_name,
organization="xCloudify",
@@ -103,7 +106,7 @@ def create_ca_for_project(project_id):
common_name=common_name,
organization="xCloudify",
organizational_unit="IT",
dns_name=common_name,
domain_name=domain_name,
validity_period=5,
is_active=True,
private_key=encrypted_private_key, # Store encrypted private key
@@ -226,17 +229,24 @@ def create_certificate():
)
# Get CA
ca = CertificateAuthority.query.get_or_404(data['ca_id'])
ca: CertificateAuthority = CertificateAuthority.query.get_or_404(data['ca_id'])
# Decrypt CA private key (in a real implementation, this would require additional authentication)
# For this example, we'll use the project_id as the password
decrypted_ca_private_key = decrypt_private_key(ca.private_key, ca.project_id)
#Just in case somsone specifies the full domain name, lets strip it all off
data['common_name']=str(data['common_name']).replace(ca.domain_name,"")
full_common_name=f"{data['common_name']}.{ca.domain_name}"
# Also check for double periods and replace with a single period
full_common_name=str(full_common_name).replace("..",".")
# Issue certificate
cert_data = issue_certificate(
ca_private_key_pem=decrypted_ca_private_key,
ca_cert_pem=ca.certificate_data,
common_name=data['common_name'],
common_name=full_common_name,
certificate_type=data['certificate_type'],
country=data.get('country'),
state=data.get('state'),
@@ -255,7 +265,7 @@ def create_certificate():
name=data['name'],
ca_id=data['ca_id'],
certificate_type=data['certificate_type'],
common_name=data['common_name'],
common_name=full_common_name,
country=data.get('country'),
state=data.get('state'),
city=data.get('city'),
@@ -413,29 +423,50 @@ def revoke_certificate_route(cert_id):
# For this example, we'll use the project_id as the password
decrypted_ca_private_key = decrypt_private_key(ca.private_key, ca.project_id)
# Revoke certificate and generate CRL
crl_data = revoke_certificate(
ca_private_key_pem=decrypted_ca_private_key,
ca_cert_pem=ca.certificate_data,
cert_pem=cert.certificate_data,
crl_number=1 # In a real implementation, this should be incremented
)
# Update certificate status
cert.revoked = True
cert.revoked_at = datetime.utcnow()
# Get all revoked certificates for this CA
revoked_certs = Certificate.query.filter_by(ca_id=ca.id, revoked=True).all()
# Prepare revoked certificates data for CRL generation
revoked_certs_data = []
for revoked_cert in revoked_certs:
# Load the certificate to get its serial number
cert_obj = x509.load_pem_x509_certificate(revoked_cert.certificate_data.encode('utf-8'))
revoked_certs_data.append((cert_obj.serial_number, revoked_cert.revoked_at))
# Generate CRL with all revoked certificates
crl_data = generate_crl_for_ca(
ca_private_key_pem=decrypted_ca_private_key,
ca_cert_pem=ca.certificate_data,
revoked_certs=revoked_certs_data,
crl_number=len(ca.crls) + 1 # Increment CRL number
)
# Create or update CRL record
# In a real implementation, you would store the CRL in the database
# For this example, we'll just log it
crl = CertificateRevocationList(
ca_id=ca.id,
crl_number=len(ca.crls) + 1,
crl_data=crl_data,
next_update=datetime.utcnow() + timedelta(days=30)
)
db.session.add(crl)
db.session.flush() # Get the ID of the new CRL
# Update CA's current CRL reference
ca.current_crl_id = crl.id
db.session.commit()
logger.info(f"Revoked certificate {cert_id}")
logger.info(f"Revoked certificate {cert_id} and updated CRL for CA {ca.id}")
return api_response(message="Certificate revoked successfully")
return api_response(message="Certificate revoked successfully and CRL updated")
except Exception as e:
logger.error(f"Error revoking certificate {cert_id}: {str(e)}")
db.session.rollback()
return api_response(
success=False,
status=500,
@@ -600,13 +631,19 @@ def get_crl(ca_id):
try:
ca = CertificateAuthority.query.get_or_404(ca_id)
# In a real implementation, you would retrieve the current CRL from storage
# For this example, we'll return a placeholder
crl_data = "-----BEGIN X509 CRL-----\n"
crl_data += "Placeholder CRL data\n"
crl_data += "-----END X509 CRL-----\n"
return crl_data, 200, {'Content-Type': 'application/x-pem-file'}
# Get the current CRL for this CA
if ca.current_crl:
crl_data = ca.current_crl.crl_data
else:
# If no CRL exists, return an empty CRL
# In a real implementation, you might want to generate one on-demand
crl_data = "-----BEGIN X509 CRL-----\n"
crl_data += "-----END X509 CRL-----\n"
headers = {
'Content-Type': 'application/x-pem-file',
'Content-Disposition': f'attachment; filename="{ca_id}-CRL.pem"'
}
return crl_data, 200,headers
except Exception as e:
logger.error(f"Error getting CRL for CA {ca_id}: {str(e)}")
return api_response(
+7 -5
View File
@@ -23,7 +23,7 @@ class CertificateAuthority(BaseModel):
city = Column(String(255), nullable=True)
organization = Column(String(255), nullable=True)
organizational_unit = Column(String(255), nullable=True)
dns_name = Column(String(255), nullable=True)
domain_name = Column(String(255), nullable=True)
validity_period = Column(Integer, default=5, nullable=False)
is_active = Column(Boolean, default=True, nullable=False)
private_key = Column(LONGTEXT, nullable=True) # Encrypted private key
@@ -31,8 +31,10 @@ class CertificateAuthority(BaseModel):
# Relationships
project = relationship("Project", backref="certificate_authority")
certificates = relationship("Certificate", back_populates="ca")
crls = relationship("CertificateRevocationList", back_populates="ca")
certificates = relationship("Certificate", back_populates="ca", foreign_keys="Certificate.ca_id")
crls = relationship("CertificateRevocationList", back_populates="ca", foreign_keys="CertificateRevocationList.ca_id")
current_crl_id = Column(String(36), ForeignKey("certificate_revocation_lists.id"), nullable=True)
current_crl = relationship("CertificateRevocationList", foreign_keys=[current_crl_id])
def to_json(self):
"""Convert the CertificateAuthority object to a JSON-serializable dictionary"""
@@ -72,7 +74,7 @@ class Certificate(BaseModel):
certificate_data = Column(LONGTEXT, nullable=True) # The actual certificate
# Relationships
ca = relationship("CertificateAuthority", back_populates="certificates")
ca = relationship("CertificateAuthority", back_populates="certificates", foreign_keys="Certificate.ca_id")
def to_json(self):
"""Convert the Certificate object to a JSON-serializable dictionary"""
@@ -99,7 +101,7 @@ class CertificateRevocationList(BaseModel):
next_update = Column(DateTime(timezone=True), nullable=False)
# Relationships
ca = relationship("CertificateAuthority", back_populates="crls")
ca = relationship("CertificateAuthority", back_populates="crls", foreign_keys="CertificateRevocationList.ca_id")
def to_json(self):
"""Convert the CertificateRevocationList object to a JSON-serializable dictionary"""
+20 -1
View File
@@ -87,7 +87,7 @@ class BaseModel(SoftDeleteMixin,db.Model):
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
visible = Column(Boolean, default=True, nullable=False)
name = Column(String(255), nullable=False)
name = Column(String(255), nullable=False, default="")
description = Column(LONGTEXT, nullable=True)
status = Column(String(50), nullable=True)
created_by = Column(String(36), ForeignKey("users.id"), nullable=True)
@@ -949,3 +949,22 @@ class WorkloadRequest(db.Model):
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
workload_type = db.Column(db.String(32))
vdc = relationship("VirtualDataCenter")
#Not used by the API, just here to satisfy Alembic
class Task(Base):
__tablename__ = "tasks"
id = Column(Integer, primary_key=True)
worker_id = Column(String(64), index=True)
job_details = Column(Text)
response = Column(Text)
status = Column(Text)
success = Column(Text)
creation_time = Column(DateTime, default=datetime.utcnow)
start_time = Column(DateTime, nullable=True)
finish_time = Column(DateTime, nullable=True)
wait_time = Column(Float, nullable=True)
execution_time = Column(Float, nullable=True)
task_type = Column(String(50), nullable=False)
depends_on = Column(Integer, nullable=True) # ID of the task this task depends on
not_before = Column(DateTime, nullable=True) # Earliest time this task can run
+57
View File
@@ -297,6 +297,8 @@ def revoke_certificate(ca_private_key_pem, ca_cert_pem, cert_pem, crl_number=1):
# Create CRL
crl_builder = x509.CertificateRevocationListBuilder().issuer_name(
ca_cert.subject
).last_update(
datetime.utcnow()
).next_update(
datetime.utcnow() + timedelta(days=30)
).add_revoked_certificate(
@@ -318,6 +320,61 @@ def revoke_certificate(ca_private_key_pem, ca_cert_pem, cert_pem, crl_number=1):
return crl_pem.decode('utf-8')
def generate_crl_for_ca(ca_private_key_pem, ca_cert_pem, revoked_certs, crl_number=1):
"""
Generate a CRL for a CA with all revoked certificates.
Args:
ca_private_key_pem (str): PEM-encoded CA private key
ca_cert_pem (str): PEM-encoded CA certificate
revoked_certs (list): List of tuples (serial_number, revocation_date)
crl_number (int): CRL number
Returns:
str: PEM-encoded CRL
"""
# Load CA private key
ca_private_key = serialization.load_pem_private_key(
ca_private_key_pem.encode('utf-8'),
password=None,
)
# Load CA certificate
ca_cert = x509.load_pem_x509_certificate(ca_cert_pem.encode('utf-8'))
# Create CRL builder
crl_builder = x509.CertificateRevocationListBuilder().issuer_name(
ca_cert.subject
).last_update(
datetime.utcnow()
).next_update(
datetime.utcnow() + timedelta(days=30)
)
# Add all revoked certificates to the CRL
for serial_number, revocation_date in revoked_certs:
revoked_cert = x509.RevokedCertificateBuilder().serial_number(
serial_number
).revocation_date(
revocation_date
).build()
crl_builder = crl_builder.add_revoked_certificate(revoked_cert)
# Add CRL number extension
crl_builder = crl_builder.add_extension(
x509.CRLNumber(crl_number),
critical=False
)
# Sign the CRL
crl = crl_builder.sign(ca_private_key, hashes.SHA256())
# Serialize CRL
crl_pem = crl.public_bytes(serialization.Encoding.PEM)
return crl_pem.decode('utf-8')
def encrypt_private_key(private_key_pem, password):
"""
Encrypt a private key using a password.
+40
View File
@@ -0,0 +1,40 @@
wget --content-disposition https://api.xcloudify.tech/api/certificates/crl/ca/5e54e03a-740d-4b6b-be11-7c7d1ff4b6d6
ubuntu@dell02:~/Downloads/code/theapi/sandbox/containers/joke_container$ openssl crl -in 5e54e03a-740d-4b6b-be11-7c7d1ff4b6d6-CRL.pem -inform pem -text -noout
Certificate Revocation List (CRL):
Version 2 (0x1)
Signature Algorithm: sha256WithRSAEncryption
Issuer: O = xCloudify, OU = IT, CN = rootca.cbf6c418-2e08-40ac-a903-48836d0d3e07.xcloudify.tech
Last Update: Aug 15 04:55:39 2025 GMT
Next Update: Sep 14 04:55:39 2025 GMT
CRL extensions:
X509v3 CRL Number:
2
Revoked Certificates:
Serial Number: 69755EFA0191D96456E99619521A1E74BB6D6F46
Revocation Date: Aug 15 04:31:16 2025 GMT
Serial Number: 1F8F1C277DB5CCDC2349CCDC929974BCAB979505
Revocation Date: Aug 15 04:55:39 2025 GMT
Serial Number: 33CDB2FA4350ACBA8B2BB35887739BFD267800EA
Revocation Date: Aug 15 04:55:33 2025 GMT
Serial Number: 6B5E04A08E6F7060F4195E9A89C2C141887EF834
Revocation Date: Aug 15 04:31:40 2025 GMT
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
51:d1:53:2c:82:2d:39:9b:38:d2:71:3b:d4:07:a6:29:c6:2a:
a7:62:bd:7f:b0:4a:e6:d7:e2:c3:18:ec:ff:83:09:68:56:23:
58:66:ae:ce:ca:03:0b:b6:eb:95:c7:6a:f2:d5:4b:a7:b7:4c:
0c:5f:6b:8f:10:3b:f9:bc:fe:1b:88:f3:ea:0d:1e:6d:a5:e9:
21:fc:af:93:91:30:4b:53:8b:21:dd:1a:9d:e5:ab:c1:a8:ee:
f3:37:d0:32:2a:78:46:8d:aa:ae:be:a5:ff:46:3d:7a:53:fe:
25:62:b6:76:49:97:e2:14:68:77:5e:b7:d2:c4:bb:0e:18:80:
a8:fa:62:a5:83:d3:0e:29:02:7a:42:4b:7f:9e:2b:50:2c:da:
f9:e4:38:cb:73:fe:e6:e5:47:4b:82:2a:f8:9f:a3:38:5d:bf:
5b:c1:cd:6f:26:df:82:55:03:4f:43:45:a3:52:ca:3d:77:de:
ac:7a:98:3a:59:69:e2:e2:1d:c3:a9:30:67:7d:33:ee:e7:88:
aa:1b:9f:0d:8f:8d:1f:22:1d:8d:7f:7d:90:f7:54:50:22:b9:
ac:b1:25:6f:12:65:21:90:52:30:64:79:eb:01:41:07:70:18:
38:7d:ad:64:b8:69:dc:ef:02:1d:ea:36:9a:39:e8:2c:54:e5:
e8:ee:e9:97
ubuntu@dell02:~/Downloads/code/theapi/sandbox/containers/joke_container$
@@ -1,8 +1,8 @@
"""First
Revision ID: 43f3c58925bf
Revision ID: 15dbb0114da7
Revises:
Create Date: 2025-08-07 06:38:28.180125
Create Date: 2025-08-15 13:36:30.771991
"""
from alembic import op
@@ -10,7 +10,7 @@ import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = '43f3c58925bf'
revision = '15dbb0114da7'
down_revision = None
branch_labels = None
depends_on = None
@@ -153,6 +153,7 @@ def upgrade():
sa.Column('ip_address_all', mysql.LONGTEXT(), nullable=True),
sa.Column('ip_address_eastwest', sa.String(length=25), nullable=True),
sa.Column('ip_address_northsouth', sa.String(length=25), nullable=True),
sa.Column('ip_address_public', sa.String(length=25), nullable=True),
sa.Column('status', sa.String(length=50), nullable=True),
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
@@ -167,6 +168,34 @@ def upgrade():
sa.ForeignKeyConstraint(['region_id'], ['regions.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('certificate_authorities',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('project_id', sa.String(length=36), nullable=False),
sa.Column('common_name', sa.String(length=255), nullable=False),
sa.Column('country', sa.String(length=2), nullable=True),
sa.Column('state', sa.String(length=255), nullable=True),
sa.Column('city', sa.String(length=255), nullable=True),
sa.Column('organization', sa.String(length=255), nullable=True),
sa.Column('organizational_unit', sa.String(length=255), nullable=True),
sa.Column('domain_name', sa.String(length=255), nullable=True),
sa.Column('validity_period', sa.Integer(), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('private_key', mysql.LONGTEXT(), nullable=True),
sa.Column('certificate_data', mysql.LONGTEXT(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.Column('visible', sa.Boolean(), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('description', mysql.LONGTEXT(), nullable=True),
sa.Column('status', sa.String(length=50), nullable=True),
sa.Column('created_by', sa.String(length=36), nullable=True),
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
sa.Column('deleted', sa.Boolean(), nullable=False),
sa.ForeignKeyConstraint(['created_by'], ['users.id'], ),
sa.ForeignKeyConstraint(['project_id'], ['projects.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('project_id')
)
op.create_table('region_access',
sa.Column('project_id', sa.String(length=36), nullable=False),
sa.Column('region_id', sa.String(length=36), nullable=False),
@@ -192,6 +221,25 @@ def upgrade():
sa.ForeignKeyConstraint(['region_id'], ['regions.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('workload_host_available_ports',
sa.Column('workload_host_id', sa.String(length=36), nullable=False),
sa.Column('start_port', sa.Integer(), nullable=False),
sa.Column('end_port', sa.Integer(), nullable=False),
sa.Column('protocol', sa.String(length=10), nullable=False),
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.Column('visible', sa.Boolean(), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('description', mysql.LONGTEXT(), nullable=True),
sa.Column('status', sa.String(length=50), nullable=True),
sa.Column('created_by', sa.String(length=36), nullable=True),
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
sa.Column('deleted', sa.Boolean(), nullable=False),
sa.ForeignKeyConstraint(['created_by'], ['users.id'], ),
sa.ForeignKeyConstraint(['workload_host_id'], ['workload_hosts.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('workload_host_fixed_resources',
sa.Column('type', sa.String(length=50), nullable=False),
sa.Column('model', sa.String(length=255), nullable=True),
@@ -249,6 +297,56 @@ def upgrade():
sa.ForeignKeyConstraint(['workload_host_id'], ['workload_hosts.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('certificate_revocation_lists',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('ca_id', sa.String(length=36), nullable=False),
sa.Column('crl_number', sa.Integer(), nullable=False),
sa.Column('crl_data', mysql.LONGTEXT(), nullable=True),
sa.Column('next_update', sa.DateTime(timezone=True), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.Column('visible', sa.Boolean(), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('description', mysql.LONGTEXT(), nullable=True),
sa.Column('status', sa.String(length=50), nullable=True),
sa.Column('created_by', sa.String(length=36), nullable=True),
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
sa.Column('deleted', sa.Boolean(), nullable=False),
sa.ForeignKeyConstraint(['ca_id'], ['certificate_authorities.id'], ),
sa.ForeignKeyConstraint(['created_by'], ['users.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('certificates',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('ca_id', sa.String(length=36), nullable=False),
sa.Column('certificate_type', sa.String(length=50), nullable=False),
sa.Column('common_name', sa.String(length=255), nullable=False),
sa.Column('country', sa.String(length=2), nullable=True),
sa.Column('state', sa.String(length=255), nullable=True),
sa.Column('city', sa.String(length=255), nullable=True),
sa.Column('organization', sa.String(length=255), nullable=True),
sa.Column('organizational_unit', sa.String(length=255), nullable=True),
sa.Column('email', sa.String(length=255), nullable=True),
sa.Column('validity_period', sa.Integer(), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('revoked', sa.Boolean(), nullable=False),
sa.Column('revoked_at', sa.DateTime(timezone=True), nullable=True),
sa.Column('public_key', mysql.LONGTEXT(), nullable=True),
sa.Column('private_key', mysql.LONGTEXT(), nullable=True),
sa.Column('certificate_data', mysql.LONGTEXT(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.Column('visible', sa.Boolean(), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('description', mysql.LONGTEXT(), nullable=True),
sa.Column('status', sa.String(length=50), nullable=True),
sa.Column('created_by', sa.String(length=36), nullable=True),
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
sa.Column('deleted', sa.Boolean(), nullable=False),
sa.ForeignKeyConstraint(['ca_id'], ['certificate_authorities.id'], ),
sa.ForeignKeyConstraint(['created_by'], ['users.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('networks',
sa.Column('vdc_id', sa.String(length=36), nullable=False),
sa.Column('ipv4_cidr', sa.String(length=50), nullable=True),
@@ -484,7 +582,8 @@ def upgrade():
sa.Column('internal_port', sa.Integer(), nullable=False),
sa.Column('external_port', sa.Integer(), nullable=False),
sa.Column('protocol', sa.String(length=10), nullable=False),
sa.Column('ip_address', sa.String(length=25), nullable=False),
sa.Column('external_ip_address', sa.String(length=25), nullable=False),
sa.Column('internal_ip_address', sa.String(length=25), nullable=False),
sa.Column('dns_record_id', sa.String(length=36), nullable=True),
sa.Column('workload_id', sa.String(length=36), nullable=True),
sa.Column('id', sa.String(length=36), nullable=False),
@@ -503,11 +602,38 @@ def upgrade():
sa.ForeignKeyConstraint(['workload_id'], ['workloads.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('tasks', schema=None) as batch_op:
batch_op.drop_index('ix_tasks_worker_id')
op.drop_table('tasks')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('tasks',
sa.Column('id', mysql.INTEGER(display_width=11), autoincrement=True, nullable=False),
sa.Column('worker_id', mysql.VARCHAR(length=64), nullable=True),
sa.Column('job_details', mysql.TEXT(), nullable=True),
sa.Column('response', mysql.TEXT(), nullable=True),
sa.Column('status', mysql.TEXT(), nullable=True),
sa.Column('success', mysql.TEXT(), nullable=True),
sa.Column('creation_time', mysql.DATETIME(), nullable=True),
sa.Column('start_time', mysql.DATETIME(), nullable=True),
sa.Column('finish_time', mysql.DATETIME(), nullable=True),
sa.Column('wait_time', mysql.FLOAT(), nullable=True),
sa.Column('execution_time', mysql.FLOAT(), nullable=True),
sa.Column('task_type', mysql.VARCHAR(length=50), nullable=False),
sa.Column('depends_on', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True),
sa.Column('not_before', mysql.DATETIME(), nullable=True),
sa.PrimaryKeyConstraint('id'),
mysql_collate='utf8mb4_general_ci',
mysql_default_charset='utf8mb4',
mysql_engine='InnoDB'
)
with op.batch_alter_table('tasks', schema=None) as batch_op:
batch_op.create_index('ix_tasks_worker_id', ['worker_id'], unique=False)
op.drop_table('port_forwarding')
op.drop_table('container_pod_containers')
op.drop_table('cloudflare_dns_records')
@@ -521,11 +647,15 @@ def downgrade():
op.drop_table('workload_requests')
op.drop_table('volumes')
op.drop_table('networks')
op.drop_table('certificates')
op.drop_table('certificate_revocation_lists')
op.drop_table('workload_host_pooled_resources')
op.drop_table('workload_host_ovs_bridge')
op.drop_table('workload_host_fixed_resources')
op.drop_table('workload_host_available_ports')
op.drop_table('vdcs')
op.drop_table('region_access')
op.drop_table('certificate_authorities')
op.drop_table('workload_hosts')
op.drop_table('projects')
op.drop_table('universes')
@@ -1,39 +0,0 @@
"""schema update
Revision ID: 2ab3b94a6452
Revises: fc7f28e036ee
Create Date: 2025-08-12 01:01:32.885734
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = '2ab3b94a6452'
down_revision = 'fc7f28e036ee'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('port_forwarding', schema=None) as batch_op:
batch_op.add_column(sa.Column('external_ip_address', sa.String(length=25), nullable=False))
batch_op.add_column(sa.Column('internal_ip_address', sa.String(length=25), nullable=False))
batch_op.drop_column('ip_address')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('port_forwarding', schema=None) as batch_op:
batch_op.add_column(sa.Column('ip_address', mysql.VARCHAR(length=25), nullable=False))
batch_op.drop_column('internal_ip_address')
batch_op.drop_column('external_ip_address')
# ### end Alembic commands ###
-113
View File
@@ -1,113 +0,0 @@
"""Certs
Revision ID: 37480d4d2782
Revises: 5fd95b9faf5f
Create Date: 2025-08-14 17:50:33.906740
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = '37480d4d2782'
down_revision = '5fd95b9faf5f'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('tasks', schema=None) as batch_op:
batch_op.drop_index('ix_tasks_worker_id')
op.drop_table('tasks')
with op.batch_alter_table('certificate_authorities', schema=None) as batch_op:
batch_op.add_column(sa.Column('visible', sa.Boolean(), nullable=False))
batch_op.add_column(sa.Column('name', sa.String(length=255), nullable=False))
batch_op.add_column(sa.Column('description', mysql.LONGTEXT(), nullable=True))
batch_op.add_column(sa.Column('status', sa.String(length=50), nullable=True))
batch_op.add_column(sa.Column('created_by', sa.String(length=36), nullable=True))
batch_op.add_column(sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True))
batch_op.add_column(sa.Column('deleted', sa.Boolean(), nullable=False))
batch_op.create_foreign_key(None, 'users', ['created_by'], ['id'])
with op.batch_alter_table('certificate_revocation_lists', schema=None) as batch_op:
batch_op.add_column(sa.Column('visible', sa.Boolean(), nullable=False))
batch_op.add_column(sa.Column('name', sa.String(length=255), nullable=False))
batch_op.add_column(sa.Column('description', mysql.LONGTEXT(), nullable=True))
batch_op.add_column(sa.Column('status', sa.String(length=50), nullable=True))
batch_op.add_column(sa.Column('created_by', sa.String(length=36), nullable=True))
batch_op.add_column(sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True))
batch_op.add_column(sa.Column('deleted', sa.Boolean(), nullable=False))
batch_op.create_foreign_key(None, 'users', ['created_by'], ['id'])
with op.batch_alter_table('certificates', schema=None) as batch_op:
batch_op.add_column(sa.Column('visible', sa.Boolean(), nullable=False))
batch_op.add_column(sa.Column('name', sa.String(length=255), nullable=False))
batch_op.add_column(sa.Column('description', mysql.LONGTEXT(), nullable=True))
batch_op.add_column(sa.Column('status', sa.String(length=50), nullable=True))
batch_op.add_column(sa.Column('created_by', sa.String(length=36), nullable=True))
batch_op.add_column(sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True))
batch_op.add_column(sa.Column('deleted', sa.Boolean(), nullable=False))
batch_op.create_foreign_key(None, 'users', ['created_by'], ['id'])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('certificates', schema=None) as batch_op:
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.drop_column('deleted')
batch_op.drop_column('deleted_at')
batch_op.drop_column('created_by')
batch_op.drop_column('status')
batch_op.drop_column('description')
batch_op.drop_column('name')
batch_op.drop_column('visible')
with op.batch_alter_table('certificate_revocation_lists', schema=None) as batch_op:
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.drop_column('deleted')
batch_op.drop_column('deleted_at')
batch_op.drop_column('created_by')
batch_op.drop_column('status')
batch_op.drop_column('description')
batch_op.drop_column('name')
batch_op.drop_column('visible')
with op.batch_alter_table('certificate_authorities', schema=None) as batch_op:
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.drop_column('deleted')
batch_op.drop_column('deleted_at')
batch_op.drop_column('created_by')
batch_op.drop_column('status')
batch_op.drop_column('description')
batch_op.drop_column('name')
batch_op.drop_column('visible')
op.create_table('tasks',
sa.Column('id', mysql.INTEGER(display_width=11), autoincrement=True, nullable=False),
sa.Column('worker_id', mysql.VARCHAR(length=64), nullable=True),
sa.Column('job_details', mysql.TEXT(), nullable=True),
sa.Column('response', mysql.TEXT(), nullable=True),
sa.Column('status', mysql.TEXT(), nullable=True),
sa.Column('success', mysql.TEXT(), nullable=True),
sa.Column('creation_time', mysql.DATETIME(), nullable=True),
sa.Column('start_time', mysql.DATETIME(), nullable=True),
sa.Column('finish_time', mysql.DATETIME(), nullable=True),
sa.Column('wait_time', mysql.FLOAT(), nullable=True),
sa.Column('execution_time', mysql.FLOAT(), nullable=True),
sa.Column('task_type', mysql.VARCHAR(length=50), nullable=False),
sa.Column('depends_on', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True),
sa.Column('not_before', mysql.DATETIME(), nullable=True),
sa.PrimaryKeyConstraint('id'),
mysql_collate='utf8mb4_general_ci',
mysql_default_charset='utf8mb4',
mysql_engine='InnoDB'
)
with op.batch_alter_table('tasks', schema=None) as batch_op:
batch_op.create_index('ix_tasks_worker_id', ['worker_id'], unique=False)
# ### end Alembic commands ###
@@ -1,46 +0,0 @@
"""Add workload_host_available_ports table
Revision ID: 5a4b8c9d1e2f
Revises: 43f3c58925bf
Create Date: 2025-08-11 06:20:00.000000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = '5a4b8c9d1e2f'
down_revision = '43f3c58925bf'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('workload_host_available_ports',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('workload_host_id', sa.String(length=36), nullable=False),
sa.Column('start_port', sa.Integer(), nullable=False),
sa.Column('end_port', sa.Integer(), nullable=False),
sa.Column('protocol', sa.String(length=10), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.Column('visible', sa.Boolean(), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('description', mysql.LONGTEXT(), nullable=True),
sa.Column('status', sa.String(length=50), nullable=True),
sa.Column('created_by', sa.String(length=36), nullable=True),
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
sa.Column('deleted', sa.Boolean(), nullable=False),
sa.ForeignKeyConstraint(['created_by'], ['users.id'], ),
sa.ForeignKeyConstraint(['workload_host_id'], ['workload_hosts.id'], ),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('workload_host_available_ports')
# ### end Alembic commands ###
@@ -1,8 +1,8 @@
"""schema update
"""Cert upd
Revision ID: fc7f28e036ee
Revises: 5a4b8c9d1e2f
Create Date: 2025-08-11 22:02:05.721807
Revision ID: 5be04054a692
Revises: 15dbb0114da7
Create Date: 2025-08-15 14:21:28.498864
"""
from alembic import op
@@ -10,8 +10,8 @@ import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = 'fc7f28e036ee'
down_revision = '5a4b8c9d1e2f'
revision = '5be04054a692'
down_revision = '15dbb0114da7'
branch_labels = None
depends_on = None
@@ -22,16 +22,18 @@ def upgrade():
batch_op.drop_index('ix_tasks_worker_id')
op.drop_table('tasks')
with op.batch_alter_table('workload_hosts', schema=None) as batch_op:
batch_op.add_column(sa.Column('ip_address_public', sa.String(length=25), nullable=True))
with op.batch_alter_table('certificate_authorities', schema=None) as batch_op:
batch_op.add_column(sa.Column('current_crl_id', sa.String(length=36), nullable=True))
batch_op.create_foreign_key(None, 'certificate_revocation_lists', ['current_crl_id'], ['id'])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('workload_hosts', schema=None) as batch_op:
batch_op.drop_column('ip_address_public')
with op.batch_alter_table('certificate_authorities', schema=None) as batch_op:
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.drop_column('current_crl_id')
op.create_table('tasks',
sa.Column('id', mysql.INTEGER(display_width=11), autoincrement=True, nullable=False),
-110
View File
@@ -1,110 +0,0 @@
"""Certs
Revision ID: 5fd95b9faf5f
Revises: 2ab3b94a6452
Create Date: 2025-08-14 15:11:31.845852
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = '5fd95b9faf5f'
down_revision = '2ab3b94a6452'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('certificate_authorities',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('project_id', sa.String(length=36), nullable=False),
sa.Column('common_name', sa.String(length=255), nullable=False),
sa.Column('country', sa.String(length=2), nullable=True),
sa.Column('state', sa.String(length=255), nullable=True),
sa.Column('city', sa.String(length=255), nullable=True),
sa.Column('organization', sa.String(length=255), nullable=True),
sa.Column('organizational_unit', sa.String(length=255), nullable=True),
sa.Column('dns_name', sa.String(length=255), nullable=True),
sa.Column('validity_period', sa.Integer(), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('private_key', mysql.LONGTEXT(), nullable=True),
sa.Column('certificate_data', mysql.LONGTEXT(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['project_id'], ['projects.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('project_id')
)
op.create_table('certificate_revocation_lists',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('ca_id', sa.String(length=36), nullable=False),
sa.Column('crl_number', sa.Integer(), nullable=False),
sa.Column('crl_data', mysql.LONGTEXT(), nullable=True),
sa.Column('next_update', sa.DateTime(timezone=True), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['ca_id'], ['certificate_authorities.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('certificates',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('ca_id', sa.String(length=36), nullable=False),
sa.Column('certificate_type', sa.String(length=50), nullable=False),
sa.Column('common_name', sa.String(length=255), nullable=False),
sa.Column('country', sa.String(length=2), nullable=True),
sa.Column('state', sa.String(length=255), nullable=True),
sa.Column('city', sa.String(length=255), nullable=True),
sa.Column('organization', sa.String(length=255), nullable=True),
sa.Column('organizational_unit', sa.String(length=255), nullable=True),
sa.Column('email', sa.String(length=255), nullable=True),
sa.Column('validity_period', sa.Integer(), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('revoked', sa.Boolean(), nullable=False),
sa.Column('revoked_at', sa.DateTime(timezone=True), nullable=True),
sa.Column('public_key', mysql.LONGTEXT(), nullable=True),
sa.Column('private_key', mysql.LONGTEXT(), nullable=True),
sa.Column('certificate_data', mysql.LONGTEXT(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['ca_id'], ['certificate_authorities.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('tasks', schema=None) as batch_op:
batch_op.drop_index('ix_tasks_worker_id')
op.drop_table('tasks')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('tasks',
sa.Column('id', mysql.INTEGER(display_width=11), autoincrement=True, nullable=False),
sa.Column('worker_id', mysql.VARCHAR(length=64), nullable=True),
sa.Column('job_details', mysql.TEXT(), nullable=True),
sa.Column('response', mysql.TEXT(), nullable=True),
sa.Column('status', mysql.TEXT(), nullable=True),
sa.Column('success', mysql.TEXT(), nullable=True),
sa.Column('creation_time', mysql.DATETIME(), nullable=True),
sa.Column('start_time', mysql.DATETIME(), nullable=True),
sa.Column('finish_time', mysql.DATETIME(), nullable=True),
sa.Column('wait_time', mysql.FLOAT(), nullable=True),
sa.Column('execution_time', mysql.FLOAT(), nullable=True),
sa.Column('task_type', mysql.VARCHAR(length=50), nullable=False),
sa.Column('depends_on', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True),
sa.Column('not_before', mysql.DATETIME(), nullable=True),
sa.PrimaryKeyConstraint('id'),
mysql_collate='utf8mb4_general_ci',
mysql_default_charset='utf8mb4',
mysql_engine='InnoDB'
)
with op.batch_alter_table('tasks', schema=None) as batch_op:
batch_op.create_index('ix_tasks_worker_id', ['worker_id'], unique=False)
op.drop_table('certificates')
op.drop_table('certificate_revocation_lists')
op.drop_table('certificate_authorities')
# ### end Alembic commands ###
@@ -1,8 +1,8 @@
"""Certs
"""Empty string default for name
Revision ID: 23306f452624
Revises: 37480d4d2782
Create Date: 2025-08-14 21:18:02.277503
Revision ID: de4cee778fa3
Revises: 5be04054a692
Create Date: 2025-08-15 14:25:22.196007
"""
from alembic import op
@@ -10,8 +10,8 @@ import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = '23306f452624'
down_revision = '37480d4d2782'
revision = 'de4cee778fa3'
down_revision = '5be04054a692'
branch_labels = None
depends_on = None
@@ -0,0 +1,15 @@
-----BEGIN X509 CRL-----
MIICXzCCAUcCAQEwDQYJKoZIhvcNAQELBQAwZjESMBAGA1UECgwJeENsb3VkaWZ5
MQswCQYDVQQLDAJJVDFDMEEGA1UEAww6cm9vdGNhLmNiZjZjNDE4LTJlMDgtNDBh
Yy1hOTAzLTQ4ODM2ZDBkM2UwNy54Y2xvdWRpZnkudGVjaBcNMjUwODE1MDQ1NTM5
WhcNMjUwOTE0MDQ1NTM5WjCBnDAlAhRpdV76AZHZZFbplhlSGh50u21vRhcNMjUw
ODE1MDQzMTE2WjAlAhQfjxwnfbXM3CNJzNySmXS8q5eVBRcNMjUwODE1MDQ1NTM5
WjAlAhQzzbL6Q1Csuosrs1iHc5v9JngA6hcNMjUwODE1MDQ1NTMzWjAlAhRrXgSg
jm9wYPQZXpqJwsFBiH74NBcNMjUwODE1MDQzMTQwWqAOMAwwCgYDVR0UBAMCAQIw
DQYJKoZIhvcNAQELBQADggEBAFHRUyyCLTmbONJxO9QHpinGKqdivX+wSubX4sMY
7P+DCWhWI1hmrs7KAwu265XHavLVS6e3TAxfa48QO/m8/huI8+oNHm2l6SH8r5OR
MEtTiyHdGp3lq8Go7vM30DIqeEaNqq6+pf9GPXpT/iVitnZJl+IUaHdet9LEuw4Y
gKj6YqWD0w4pAnpCS3+eK1As2vnkOMtz/ublR0uCKvifozhdv1vBzW8m34JVA09D
RaNSyj133qx6mDpZaeLiHcOpMGd9M+7niKobnw2PjR8iHY1/fZD3VFAiuayxJW8S
ZSGQUjBkeesBQQdwGDh9rWS4adzvAh3qNpo56CxU5eju6Zc=
-----END X509 CRL-----
@@ -0,0 +1,15 @@
-----BEGIN X509 CRL-----
MIICXzCCAUcCAQEwDQYJKoZIhvcNAQELBQAwZjESMBAGA1UECgwJeENsb3VkaWZ5
MQswCQYDVQQLDAJJVDFDMEEGA1UEAww6cm9vdGNhLmNiZjZjNDE4LTJlMDgtNDBh
Yy1hOTAzLTQ4ODM2ZDBkM2UwNy54Y2xvdWRpZnkudGVjaBcNMjUwODE1MDQ1NTM5
WhcNMjUwOTE0MDQ1NTM5WjCBnDAlAhRpdV76AZHZZFbplhlSGh50u21vRhcNMjUw
ODE1MDQzMTE2WjAlAhQfjxwnfbXM3CNJzNySmXS8q5eVBRcNMjUwODE1MDQ1NTM5
WjAlAhQzzbL6Q1Csuosrs1iHc5v9JngA6hcNMjUwODE1MDQ1NTMzWjAlAhRrXgSg
jm9wYPQZXpqJwsFBiH74NBcNMjUwODE1MDQzMTQwWqAOMAwwCgYDVR0UBAMCAQIw
DQYJKoZIhvcNAQELBQADggEBAFHRUyyCLTmbONJxO9QHpinGKqdivX+wSubX4sMY
7P+DCWhWI1hmrs7KAwu265XHavLVS6e3TAxfa48QO/m8/huI8+oNHm2l6SH8r5OR
MEtTiyHdGp3lq8Go7vM30DIqeEaNqq6+pf9GPXpT/iVitnZJl+IUaHdet9LEuw4Y
gKj6YqWD0w4pAnpCS3+eK1As2vnkOMtz/ublR0uCKvifozhdv1vBzW8m34JVA09D
RaNSyj133qx6mDpZaeLiHcOpMGd9M+7niKobnw2PjR8iHY1/fZD3VFAiuayxJW8S
ZSGQUjBkeesBQQdwGDh9rWS4adzvAh3qNpo56CxU5eju6Zc=
-----END X509 CRL-----
@@ -0,0 +1,15 @@
-----BEGIN X509 CRL-----
MIICXzCCAUcCAQEwDQYJKoZIhvcNAQELBQAwZjESMBAGA1UECgwJeENsb3VkaWZ5
MQswCQYDVQQLDAJJVDFDMEEGA1UEAww6cm9vdGNhLmNiZjZjNDE4LTJlMDgtNDBh
Yy1hOTAzLTQ4ODM2ZDBkM2UwNy54Y2xvdWRpZnkudGVjaBcNMjUwODE1MDQ1NTM5
WhcNMjUwOTE0MDQ1NTM5WjCBnDAlAhRpdV76AZHZZFbplhlSGh50u21vRhcNMjUw
ODE1MDQzMTE2WjAlAhQfjxwnfbXM3CNJzNySmXS8q5eVBRcNMjUwODE1MDQ1NTM5
WjAlAhQzzbL6Q1Csuosrs1iHc5v9JngA6hcNMjUwODE1MDQ1NTMzWjAlAhRrXgSg
jm9wYPQZXpqJwsFBiH74NBcNMjUwODE1MDQzMTQwWqAOMAwwCgYDVR0UBAMCAQIw
DQYJKoZIhvcNAQELBQADggEBAFHRUyyCLTmbONJxO9QHpinGKqdivX+wSubX4sMY
7P+DCWhWI1hmrs7KAwu265XHavLVS6e3TAxfa48QO/m8/huI8+oNHm2l6SH8r5OR
MEtTiyHdGp3lq8Go7vM30DIqeEaNqq6+pf9GPXpT/iVitnZJl+IUaHdet9LEuw4Y
gKj6YqWD0w4pAnpCS3+eK1As2vnkOMtz/ublR0uCKvifozhdv1vBzW8m34JVA09D
RaNSyj133qx6mDpZaeLiHcOpMGd9M+7niKobnw2PjR8iHY1/fZD3VFAiuayxJW8S
ZSGQUjBkeesBQQdwGDh9rWS4adzvAh3qNpo56CxU5eju6Zc=
-----END X509 CRL-----
@@ -0,0 +1,15 @@
-----BEGIN X509 CRL-----
MIICXzCCAUcCAQEwDQYJKoZIhvcNAQELBQAwZjESMBAGA1UECgwJeENsb3VkaWZ5
MQswCQYDVQQLDAJJVDFDMEEGA1UEAww6cm9vdGNhLmNiZjZjNDE4LTJlMDgtNDBh
Yy1hOTAzLTQ4ODM2ZDBkM2UwNy54Y2xvdWRpZnkudGVjaBcNMjUwODE1MDQ1NTM5
WhcNMjUwOTE0MDQ1NTM5WjCBnDAlAhRpdV76AZHZZFbplhlSGh50u21vRhcNMjUw
ODE1MDQzMTE2WjAlAhQfjxwnfbXM3CNJzNySmXS8q5eVBRcNMjUwODE1MDQ1NTM5
WjAlAhQzzbL6Q1Csuosrs1iHc5v9JngA6hcNMjUwODE1MDQ1NTMzWjAlAhRrXgSg
jm9wYPQZXpqJwsFBiH74NBcNMjUwODE1MDQzMTQwWqAOMAwwCgYDVR0UBAMCAQIw
DQYJKoZIhvcNAQELBQADggEBAFHRUyyCLTmbONJxO9QHpinGKqdivX+wSubX4sMY
7P+DCWhWI1hmrs7KAwu265XHavLVS6e3TAxfa48QO/m8/huI8+oNHm2l6SH8r5OR
MEtTiyHdGp3lq8Go7vM30DIqeEaNqq6+pf9GPXpT/iVitnZJl+IUaHdet9LEuw4Y
gKj6YqWD0w4pAnpCS3+eK1As2vnkOMtz/ublR0uCKvifozhdv1vBzW8m34JVA09D
RaNSyj133qx6mDpZaeLiHcOpMGd9M+7niKobnw2PjR8iHY1/fZD3VFAiuayxJW8S
ZSGQUjBkeesBQQdwGDh9rWS4adzvAh3qNpo56CxU5eju6Zc=
-----END X509 CRL-----
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script type="module">import { injectIntoGlobalHook } from "/@react-refresh";
injectIntoGlobalHook(window);
window.$RefreshReg$ = () => {};
window.$RefreshSig$ = () => (type) => type;</script>
<script type="module" src="/@vite/client"></script>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GPU Cloud - IaaS Portal</title>
<meta name="description" content="IaaS Portal for GPU and Docker containers management" />
<meta name="author" content="Lovable" />
<meta property="og:title" content="GPU Cloud - IaaS Portal" />
<meta property="og:description" content="IaaS Portal for GPU and Docker containers management" />
<meta property="og:type" content="website" />
<meta property="og:image" content="https://lovable.dev/opengraph-image-p98pqg.png" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@lovable_dev" />
<meta name="twitter:image" content="https://lovable.dev/opengraph-image-p98pqg.png" />
</head>
<body>
<div id="root"></div><script src="https://cdn.gpteng.co/lovable.js" type="module"></script>
<!-- IMPORTANT: DO NOT REMOVE THIS SCRIPT TAG OR THIS VERY COMMENT! -->
<script type="module" src="/src/main.tsx?t=1755233128858"></script>
</body>
</html>
+1 -1
View File
@@ -29,7 +29,7 @@ def redis_subscribe(worker_id):
f"__keyspace@2__:worker_status_{worker_id}", #TODO - The number 2 here is important, it's the Redis DB number which must align with the resid URL in config.py!
f"__keyspace@2__:worker_queue_{worker_id}"
]
logger.error(f"Subscribed to {channels}")
try:
pubsub.psubscribe(channels)
logger.info(f"Subscribed to Redis keyspace events: {channels}")