mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-08 21:14:47 +09:00
- ImageUpdateCheck model + ImageUpdateService: compare an app's local RepoDigest against the registry's current index digest (via `docker buildx imagetools inspect`) for the same tag, and flag update_available when they differ. Shells out to docker like the rest of the Docker layer; degrades gracefully when the image is local-only or the registry is unreachable. - API: admin POST /api/v1/image-updates/applications/<id>/check and GET /api/v1/image-updates/applications/<id> for the latest result. - Application.to_dict gains a lightweight image_update badge (mirrors the existing image_scan badge). - 7 tests: digest diff/match/unknown, no-image, badge, endpoint, auth gate. One-click apply (pull + recreate) to follow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
36 lines
1.5 KiB
Python
36 lines
1.5 KiB
Python
from datetime import datetime
|
|
from app import db
|
|
|
|
|
|
class ImageUpdateCheck(db.Model):
|
|
"""Result of comparing an application's running image digest against the
|
|
current digest for the same tag in its registry."""
|
|
__tablename__ = 'image_update_checks'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
application_id = db.Column(db.Integer, db.ForeignKey('applications.id'), nullable=False, index=True)
|
|
image_ref = db.Column(db.String(500), nullable=False)
|
|
current_digest = db.Column(db.String(128)) # sha256:... running locally
|
|
latest_digest = db.Column(db.String(128)) # sha256:... currently in the registry
|
|
update_available = db.Column(db.Boolean, default=False, nullable=False)
|
|
status = db.Column(db.String(20), default='pending') # pending, completed, failed
|
|
error_message = db.Column(db.Text)
|
|
checked_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
|
|
application = db.relationship('Application', backref=db.backref(
|
|
'image_update_checks', lazy='dynamic',
|
|
order_by='ImageUpdateCheck.checked_at.desc()'))
|
|
|
|
def to_dict(self):
|
|
return {
|
|
'id': self.id,
|
|
'application_id': self.application_id,
|
|
'image_ref': self.image_ref,
|
|
'current_digest': self.current_digest,
|
|
'latest_digest': self.latest_digest,
|
|
'update_available': self.update_available,
|
|
'status': self.status,
|
|
'error_message': self.error_message,
|
|
'checked_at': self.checked_at.isoformat() if self.checked_at else None,
|
|
}
|