1
0
mirror of https://github.com/quay/quay.git synced 2025-07-31 18:44:32 +03:00

chore: allows Quay to run for account recoveries (PROJQUAY-970) (#793)

Adds ACCOUNT_RECOVERY_MODE to allow Quay to run with some core
features disabled. When this is set, the instance should only be used
in order by existing users who hasn't linked their account to an
external login service, after database authentication has been
disabled.
This commit is contained in:
Kenny Lee Sin Cheong
2021-07-07 12:45:24 -04:00
committed by GitHub
parent 95ec9478fc
commit a839a78eb5
34 changed files with 178 additions and 11 deletions

View File

@ -34,17 +34,20 @@ DEFAULT_CONTROLLER_PORT = 8686
def run_build_manager(): def run_build_manager():
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
if not features.BUILD_SUPPORT: if not features.BUILD_SUPPORT:
logger.debug("Building is disabled. Please enable the feature flag") logger.debug("Building is disabled. Please enable the feature flag")
while True: while True:
time.sleep(1000) time.sleep(1000)
return
if app.config.get("REGISTRY_STATE", "normal") == "readonly": if app.config.get("REGISTRY_STATE", "normal") == "readonly":
logger.debug("Building is disabled while in read-only mode.") logger.debug("Building is disabled while in read-only mode.")
while True: while True:
time.sleep(1000) time.sleep(1000)
return
build_manager_config = app.config.get("BUILD_MANAGER") build_manager_config = app.config.get("BUILD_MANAGER")
if build_manager_config is None: if build_manager_config is None:

View File

@ -773,3 +773,6 @@ class DefaultConfig(ImmutableConfig):
# Create organization on push if it does not exist # Create organization on push if it does not exist
CREATE_NAMESPACE_ON_PUSH = False CREATE_NAMESPACE_ON_PUSH = False
# Account recovery mode
ACCOUNT_RECOVERY_MODE = False

View File

@ -158,6 +158,7 @@ def render_page_template(name, route_data=None, **kwargs):
version_number=version_number, version_number=version_number,
current_year=datetime.datetime.now().year, current_year=datetime.datetime.now().year,
kubernetes_namespace=IS_KUBERNETES and QE_NAMESPACE, kubernetes_namespace=IS_KUBERNETES and QE_NAMESPACE,
account_recovery_mode=app.config.get("ACCOUNT_RECOVERY_MODE", False),
**kwargs, **kwargs,
) )

View File

@ -171,6 +171,21 @@ def route_show_if(value):
return decorator return decorator
def disallow_for_account_recovery_mode(func):
"""
Disable route if ACCOUNT_RECOVERY_MODE is set.
"""
@wraps(func)
def wrapper(*args, **kwargs):
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
abort(405, "Quay running for account recoveries only.")
return func(*args, **kwargs)
return wrapper
def require_xhr_from_browser(func): def require_xhr_from_browser(func):
""" """
Requires that API GET calls made from browsers are made via XHR, in order to prevent reflected Requires that API GET calls made from browsers are made via XHR, in order to prevent reflected

View File

@ -21,6 +21,7 @@ from digest import digest_tools
from endpoints.decorators import ( from endpoints.decorators import (
anon_protect, anon_protect,
anon_allowed, anon_allowed,
disallow_for_account_recovery_mode,
parse_repository_name, parse_repository_name,
check_region_blacklisted, check_region_blacklisted,
check_readonly, check_readonly,
@ -51,6 +52,7 @@ BLOB_CONTENT_TYPE = "application/octet-stream"
@v2_bp.route(BLOB_DIGEST_ROUTE, methods=["HEAD"]) @v2_bp.route(BLOB_DIGEST_ROUTE, methods=["HEAD"])
@disallow_for_account_recovery_mode
@parse_repository_name() @parse_repository_name()
@process_registry_jwt_auth(scopes=["pull"]) @process_registry_jwt_auth(scopes=["pull"])
@require_repo_read @require_repo_read
@ -78,6 +80,7 @@ def check_blob_exists(namespace_name, repo_name, digest):
@v2_bp.route(BLOB_DIGEST_ROUTE, methods=["GET"]) @v2_bp.route(BLOB_DIGEST_ROUTE, methods=["GET"])
@disallow_for_account_recovery_mode
@parse_repository_name() @parse_repository_name()
@process_registry_jwt_auth(scopes=["pull"]) @process_registry_jwt_auth(scopes=["pull"])
@require_repo_read @require_repo_read
@ -217,6 +220,7 @@ def _try_to_mount_blob(repository_ref, mount_blob_digest):
@v2_bp.route("/<repopath:repository>/blobs/uploads/", methods=["POST"]) @v2_bp.route("/<repopath:repository>/blobs/uploads/", methods=["POST"])
@disallow_for_account_recovery_mode
@parse_repository_name() @parse_repository_name()
@process_registry_jwt_auth(scopes=["pull", "push"]) @process_registry_jwt_auth(scopes=["pull", "push"])
@require_repo_write @require_repo_write
@ -278,6 +282,7 @@ def start_blob_upload(namespace_name, repo_name):
@v2_bp.route("/<repopath:repository>/blobs/uploads/<upload_uuid>", methods=["GET"]) @v2_bp.route("/<repopath:repository>/blobs/uploads/<upload_uuid>", methods=["GET"])
@disallow_for_account_recovery_mode
@parse_repository_name() @parse_repository_name()
@process_registry_jwt_auth(scopes=["pull"]) @process_registry_jwt_auth(scopes=["pull"])
@require_repo_write @require_repo_write
@ -305,6 +310,7 @@ def fetch_existing_upload(namespace_name, repo_name, upload_uuid):
@v2_bp.route("/<repopath:repository>/blobs/uploads/<upload_uuid>", methods=["PATCH"]) @v2_bp.route("/<repopath:repository>/blobs/uploads/<upload_uuid>", methods=["PATCH"])
@disallow_for_account_recovery_mode
@parse_repository_name() @parse_repository_name()
@process_registry_jwt_auth(scopes=["pull", "push"]) @process_registry_jwt_auth(scopes=["pull", "push"])
@require_repo_write @require_repo_write
@ -336,6 +342,7 @@ def upload_chunk(namespace_name, repo_name, upload_uuid):
@v2_bp.route("/<repopath:repository>/blobs/uploads/<upload_uuid>", methods=["PUT"]) @v2_bp.route("/<repopath:repository>/blobs/uploads/<upload_uuid>", methods=["PUT"])
@disallow_for_account_recovery_mode
@parse_repository_name() @parse_repository_name()
@process_registry_jwt_auth(scopes=["pull", "push"]) @process_registry_jwt_auth(scopes=["pull", "push"])
@require_repo_write @require_repo_write
@ -376,6 +383,7 @@ def monolithic_upload_or_last_chunk(namespace_name, repo_name, upload_uuid):
@v2_bp.route("/<repopath:repository>/blobs/uploads/<upload_uuid>", methods=["DELETE"]) @v2_bp.route("/<repopath:repository>/blobs/uploads/<upload_uuid>", methods=["DELETE"])
@disallow_for_account_recovery_mode
@parse_repository_name() @parse_repository_name()
@process_registry_jwt_auth(scopes=["pull", "push"]) @process_registry_jwt_auth(scopes=["pull", "push"])
@require_repo_write @require_repo_write
@ -397,6 +405,7 @@ def cancel_upload(namespace_name, repo_name, upload_uuid):
@v2_bp.route("/<repopath:repository>/blobs/<digest>", methods=["DELETE"]) @v2_bp.route("/<repopath:repository>/blobs/<digest>", methods=["DELETE"])
@disallow_for_account_recovery_mode
@parse_repository_name() @parse_repository_name()
@process_registry_jwt_auth(scopes=["pull", "push"]) @process_registry_jwt_auth(scopes=["pull", "push"])
@require_repo_write @require_repo_write

View File

@ -9,7 +9,7 @@ from auth.auth_context import get_authenticated_user, get_authenticated_context
from auth.registry_jwt_auth import process_registry_jwt_auth from auth.registry_jwt_auth import process_registry_jwt_auth
from data import model from data import model
from data.cache import cache_key from data.cache import cache_key
from endpoints.decorators import anon_protect from endpoints.decorators import anon_protect, disallow_for_account_recovery_mode, route_show_if
from endpoints.v2 import v2_bp, paginate from endpoints.v2 import v2_bp, paginate
@ -18,6 +18,7 @@ class Repository(namedtuple("Repository", ["id", "namespace_name", "name"])):
@v2_bp.route("/_catalog", methods=["GET"]) @v2_bp.route("/_catalog", methods=["GET"])
@disallow_for_account_recovery_mode
@process_registry_jwt_auth() @process_registry_jwt_auth()
@anon_protect @anon_protect
@paginate() @paginate()

View File

@ -13,7 +13,12 @@ from data.database import db_disallow_replica_use
from data.registry_model import registry_model from data.registry_model import registry_model
from data.model.oci.manifest import CreateManifestException from data.model.oci.manifest import CreateManifestException
from data.model.oci.tag import RetargetTagException from data.model.oci.tag import RetargetTagException
from endpoints.decorators import anon_protect, parse_repository_name, check_readonly from endpoints.decorators import (
anon_protect,
disallow_for_account_recovery_mode,
parse_repository_name,
check_readonly,
)
from endpoints.metrics import image_pulls, image_pushes from endpoints.metrics import image_pulls, image_pushes
from endpoints.v2 import v2_bp, require_repo_read, require_repo_write from endpoints.v2 import v2_bp, require_repo_read, require_repo_write
from endpoints.v2.errors import ( from endpoints.v2.errors import (
@ -43,6 +48,7 @@ MANIFEST_TAGNAME_ROUTE = BASE_MANIFEST_ROUTE.format(VALID_TAG_PATTERN)
@v2_bp.route(MANIFEST_TAGNAME_ROUTE, methods=["GET"]) @v2_bp.route(MANIFEST_TAGNAME_ROUTE, methods=["GET"])
@disallow_for_account_recovery_mode
@parse_repository_name() @parse_repository_name()
@process_registry_jwt_auth(scopes=["pull"]) @process_registry_jwt_auth(scopes=["pull"])
@require_repo_read @require_repo_read
@ -101,6 +107,7 @@ def fetch_manifest_by_tagname(namespace_name, repo_name, manifest_ref):
@v2_bp.route(MANIFEST_DIGEST_ROUTE, methods=["GET"]) @v2_bp.route(MANIFEST_DIGEST_ROUTE, methods=["GET"])
@disallow_for_account_recovery_mode
@parse_repository_name() @parse_repository_name()
@process_registry_jwt_auth(scopes=["pull"]) @process_registry_jwt_auth(scopes=["pull"])
@require_repo_read @require_repo_read
@ -213,6 +220,7 @@ def _doesnt_accept_schema_v1():
@v2_bp.route(MANIFEST_TAGNAME_ROUTE, methods=["PUT"]) @v2_bp.route(MANIFEST_TAGNAME_ROUTE, methods=["PUT"])
@disallow_for_account_recovery_mode
@parse_repository_name() @parse_repository_name()
@_reject_manifest2_schema2 @_reject_manifest2_schema2
@process_registry_jwt_auth(scopes=["pull", "push"]) @process_registry_jwt_auth(scopes=["pull", "push"])
@ -225,6 +233,7 @@ def write_manifest_by_tagname(namespace_name, repo_name, manifest_ref):
@v2_bp.route(MANIFEST_DIGEST_ROUTE, methods=["PUT"]) @v2_bp.route(MANIFEST_DIGEST_ROUTE, methods=["PUT"])
@disallow_for_account_recovery_mode
@parse_repository_name() @parse_repository_name()
@_reject_manifest2_schema2 @_reject_manifest2_schema2
@process_registry_jwt_auth(scopes=["pull", "push"]) @process_registry_jwt_auth(scopes=["pull", "push"])
@ -285,6 +294,7 @@ def _parse_manifest():
@v2_bp.route(MANIFEST_DIGEST_ROUTE, methods=["DELETE"]) @v2_bp.route(MANIFEST_DIGEST_ROUTE, methods=["DELETE"])
@disallow_for_account_recovery_mode
@parse_repository_name() @parse_repository_name()
@process_registry_jwt_auth(scopes=["pull", "push"]) @process_registry_jwt_auth(scopes=["pull", "push"])
@require_repo_write @require_repo_write

View File

@ -1,14 +1,20 @@
from flask import jsonify from flask import jsonify
from app import model_cache from app import app, model_cache
from auth.registry_jwt_auth import process_registry_jwt_auth from auth.registry_jwt_auth import process_registry_jwt_auth
from data.registry_model import registry_model from data.registry_model import registry_model
from endpoints.decorators import anon_protect, parse_repository_name from endpoints.decorators import (
anon_protect,
disallow_for_account_recovery_mode,
parse_repository_name,
route_show_if,
)
from endpoints.v2 import v2_bp, require_repo_read, paginate from endpoints.v2 import v2_bp, require_repo_read, paginate
from endpoints.v2.errors import NameUnknown from endpoints.v2.errors import NameUnknown
@v2_bp.route("/<repopath:repository>/tags/list", methods=["GET"]) @v2_bp.route("/<repopath:repository>/tags/list", methods=["GET"])
@disallow_for_account_recovery_mode
@parse_repository_name() @parse_repository_name()
@process_registry_jwt_auth(scopes=["pull"]) @process_registry_jwt_auth(scopes=["pull"])
@require_repo_read @require_repo_read

View File

@ -1,5 +1,5 @@
from app import app as application from app import app as application
from endpoints.secscan import secscan from endpoints.secscan import secscan
if not application.config.get("ACCOUNT_RECOVERY_MODE", False):
application.register_blueprint(secscan, url_prefix="/secscan") application.register_blueprint(secscan, url_prefix="/secscan")

View File

@ -101,4 +101,4 @@
</div><!-- /.modal-content --> </div><!-- /.modal-content -->
</div><!-- /.modal-dialog --> </div><!-- /.modal-dialog -->
</div><!-- /.modal --> </div><!-- /.modal -->
</div> </div>

View File

@ -1,10 +1,17 @@
<div class="announcement inline quay-message-bar-element" ng-show="messages.length || inReadOnlyMode"> <div class="announcement inline quay-message-bar-element" ng-show="messages.length || inReadOnlyMode || inAccountRecoveryMode">
<div class="quay-service-status-description info" ng-if="inReadOnlyMode"> <div class="quay-service-status-description info" ng-if="inReadOnlyMode">
<div style="display: inline-block"> <div style="display: inline-block">
<span class="registry-name"></span>&nbsp;is currently in read-only mode. Pulls and other read-only operations <span class="registry-name"></span>&nbsp;is currently in read-only mode. Pulls and other read-only operations
will succeed but all other operations are currently suspended. will succeed but all other operations are currently suspended.
</div> </div>
</div> </div>
<div class="quay-service-status-description warning" ng-if="inAccountRecoveryMode">
<div style="display: inline-block">
<span class="registry-name"></span>&nbsp;is currently in account recovery mode. This instance should only be
used to link accounts to an external login service. e.g RedHat. Registry operations such as pushes/pulls
will not work.
</div>
</div>
<div ng-repeat="token in NotificationService.expiringAppTokens"> <div ng-repeat="token in NotificationService.expiringAppTokens">
<div class="quay-service-status-description warning"> <div class="quay-service-status-description warning">
Your external application token <strong style="display: inline-block; padding: 4px;">{{ token.title }}</strong> Your external application token <strong style="display: inline-block; padding: 4px;">{{ token.title }}</strong>

View File

@ -16,6 +16,7 @@ angular.module('quay').directive('quayMessageBar', function () {
StateService.updateStateIn($scope, function(state) { StateService.updateStateIn($scope, function(state) {
$scope.inReadOnlyMode = state.inReadOnlyMode; $scope.inReadOnlyMode = state.inReadOnlyMode;
$scope.inAccountRecoveryMode = state.inAccountRecoveryMode;
}); });
ApiService.getGlobalMessages().then(function (data) { ApiService.getGlobalMessages().then(function (data) {

View File

@ -33,6 +33,10 @@ export function provideRun($rootScope: QuayRunScope,
stateService.setInReadOnlyMode(); stateService.setInReadOnlyMode();
} }
if ((<any>window).__account_recovery_mode) {
stateService.setInAccountRecoveryMode();
}
// Handle session security. // Handle session security.
restangular.setDefaultHeaders({ restangular.setDefaultHeaders({
'X-Requested-With': 'XMLHttpRequest', 'X-Requested-With': 'XMLHttpRequest',

View File

@ -6,7 +6,8 @@ angular.module('quay')
var stateService = {}; var stateService = {};
var currentState = { var currentState = {
'inReadOnlyMode': false 'inReadOnlyMode': false,
'inAccountRecoveryMode': false
}; };
stateService.inReadOnlyMode = function() { stateService.inReadOnlyMode = function() {
@ -17,6 +18,15 @@ angular.module('quay')
currentState.inReadOnlyMode = true; currentState.inReadOnlyMode = true;
}; };
stateService.inAccountRecoveryMode = function() {
return currentState.inAccountRecoveryMode;
};
stateService.setInAccountRecoveryMode = function() {
currentState.inAccountRecoveryMode = true;
};
stateService.updateStateIn = function(scope, opt_callback) { stateService.updateStateIn = function(scope, opt_callback) {
scope.$watch(function () { return stateService.currentState(); }, function (currentState) { scope.$watch(function () { return stateService.currentState(); }, function (currentState) {
$timeout(function(){ $timeout(function(){

View File

@ -47,6 +47,7 @@
window.__token = '{{ csrf_token() }}'; window.__token = '{{ csrf_token() }}';
window.__kubernetes_namespace = {{ kubernetes_namespace|tojson|safe }}; window.__kubernetes_namespace = {{ kubernetes_namespace|tojson|safe }};
window.__registry_state = '{{ registry_state }}'; window.__registry_state = '{{ registry_state }}';
window.__account_recovery_mode = '{{ account_recovery_mode }}';
{% if error_code %} {% if error_code %}
window.__error_code = {{ error_code }}; window.__error_code = {{ error_code }};

View File

@ -99,6 +99,7 @@ INTERNAL_ONLY_PROPERTIES = {
"LOGS_MODEL_CONFIG", "LOGS_MODEL_CONFIG",
"APP_REGISTRY_RESULTS_LIMIT", "APP_REGISTRY_RESULTS_LIMIT",
"V3_UPGRADE_MODE", # Deprecated old flag "V3_UPGRADE_MODE", # Deprecated old flag
"ACCOUNT_RECOVERY_MODE",
} }
CONFIG_SCHEMA = { CONFIG_SCHEMA = {

View File

@ -1,5 +1,6 @@
import logging import logging
import logging.config import logging.config
import time
from datetime import timedelta, datetime from datetime import timedelta, datetime
@ -85,6 +86,11 @@ def create_gunicorn_worker():
if __name__ == "__main__": if __name__ == "__main__":
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False) logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False)
GlobalLock.configure(app.config) GlobalLock.configure(app.config)
worker = BlobUploadCleanupWorker() worker = BlobUploadCleanupWorker()

View File

@ -1,4 +1,5 @@
import logging import logging
import time
from gzip import GzipFile from gzip import GzipFile
from tempfile import SpooledTemporaryFile from tempfile import SpooledTemporaryFile
@ -79,5 +80,10 @@ def create_gunicorn_worker():
if __name__ == "__main__": if __name__ == "__main__":
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
worker = ArchiveBuildLogsWorker() worker = ArchiveBuildLogsWorker()
worker.start() worker.start()

View File

@ -59,6 +59,11 @@ def create_gunicorn_worker():
if __name__ == "__main__": if __name__ == "__main__":
logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False) logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False)
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
engines = set( engines = set(
[config[0] for config in list(app.config.get("DISTRIBUTED_STORAGE_CONFIG", {}).values())] [config[0] for config in list(app.config.get("DISTRIBUTED_STORAGE_CONFIG", {}).values())]
) )

View File

@ -56,6 +56,11 @@ def create_gunicorn_worker():
if __name__ == "__main__": if __name__ == "__main__":
logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False) logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False)
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
if not features.APP_SPECIFIC_TOKENS: if not features.APP_SPECIFIC_TOKENS:
logger.debug("App specific tokens disabled; skipping") logger.debug("App specific tokens disabled; skipping")
while True: while True:

View File

@ -86,6 +86,11 @@ def create_gunicorn_worker():
if __name__ == "__main__": if __name__ == "__main__":
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
if not features.GARBAGE_COLLECTION: if not features.GARBAGE_COLLECTION:
logger.debug("Garbage collection is disabled; skipping") logger.debug("Garbage collection is disabled; skipping")
while True: while True:

View File

@ -82,6 +82,11 @@ def create_gunicorn_worker():
def main(): def main():
logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False) logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False)
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
if not app.config.get("PROMETHEUS_PUSHGATEWAY_URL"): if not app.config.get("PROMETHEUS_PUSHGATEWAY_URL"):
logger.debug("Prometheus not enabled; skipping global stats reporting") logger.debug("Prometheus not enabled; skipping global stats reporting")
while True: while True:

View File

@ -148,6 +148,11 @@ def create_gunicorn_worker():
def main(): def main():
logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False) logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False)
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
if not features.ACTION_LOG_ROTATION or None in [SAVE_PATH, SAVE_LOCATION]: if not features.ACTION_LOG_ROTATION or None in [SAVE_PATH, SAVE_LOCATION]:
logger.debug("Action log rotation worker not enabled; skipping") logger.debug("Action log rotation worker not enabled; skipping")
while True: while True:

View File

@ -1,4 +1,5 @@
import logging import logging
import time
from peewee import fn from peewee import fn
@ -103,6 +104,11 @@ def create_gunicorn_worker():
def main(): def main():
logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False) logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False)
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
if not features.MANIFEST_SIZE_BACKFILL: if not features.MANIFEST_SIZE_BACKFILL:
logger.debug("Manifest backfill worker not enabled; skipping") logger.debug("Manifest backfill worker not enabled; skipping")
while True: while True:

View File

@ -64,6 +64,11 @@ def create_gunicorn_worker():
if __name__ == "__main__": if __name__ == "__main__":
logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False) logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False)
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
if not features.NAMESPACE_GARBAGE_COLLECTION: if not features.NAMESPACE_GARBAGE_COLLECTION:
logger.debug("Namespace garbage collection is disabled; skipping") logger.debug("Namespace garbage collection is disabled; skipping")
while True: while True:

View File

@ -1,4 +1,5 @@
import logging import logging
import time
from app import app, notification_queue from app import app, notification_queue
from notifications.notificationmethod import NotificationMethod, InvalidNotificationMethodException from notifications.notificationmethod import NotificationMethod, InvalidNotificationMethodException
@ -55,6 +56,11 @@ def create_gunicorn_worker():
if __name__ == "__main__": if __name__ == "__main__":
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
worker = NotificationWorker( worker = NotificationWorker(
notification_queue, poll_period_seconds=10, reservation_seconds=30, retry_after_seconds=30 notification_queue, poll_period_seconds=10, reservation_seconds=30, retry_after_seconds=30
) )

View File

@ -1,4 +1,5 @@
import logging import logging
import time
from datetime import timedelta, datetime from datetime import timedelta, datetime
@ -51,5 +52,10 @@ def create_gunicorn_worker():
if __name__ == "__main__": if __name__ == "__main__":
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
worker = QueueCleanupWorker() worker = QueueCleanupWorker()
worker.start() worker.start()

View File

@ -68,6 +68,11 @@ if __name__ == "__main__":
) )
args = parser.parse_args() args = parser.parse_args()
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
if not features.REPO_MIRROR: if not features.REPO_MIRROR:
logger.debug("Repository mirror disabled; skipping RepoMirrorWorker") logger.debug("Repository mirror disabled; skipping RepoMirrorWorker")
while True: while True:

View File

@ -121,6 +121,11 @@ def create_gunicorn_worker():
if __name__ == "__main__": if __name__ == "__main__":
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
if not features.REPOSITORY_ACTION_COUNTER: if not features.REPOSITORY_ACTION_COUNTER:
logger.info("Repository action count is disabled; skipping") logger.info("Repository action count is disabled; skipping")
while True: while True:

View File

@ -70,6 +70,11 @@ def create_gunicorn_worker():
if __name__ == "__main__": if __name__ == "__main__":
logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False) logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False)
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
if not features.REPOSITORY_GARBAGE_COLLECTION: if not features.REPOSITORY_GARBAGE_COLLECTION:
logger.info("Repository garbage collection is disabled; skipping") logger.info("Repository garbage collection is disabled; skipping")
while True: while True:

View File

@ -152,6 +152,11 @@ def create_gunicorn_worker():
if __name__ == "__main__": if __name__ == "__main__":
logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False) logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False)
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
if not features.SECURITY_SCANNER: if not features.SECURITY_SCANNER:
logger.debug("Security scanner disabled; sleeping") logger.debug("Security scanner disabled; sleeping")
while True: while True:

View File

@ -57,6 +57,11 @@ if __name__ == "__main__":
app.register_blueprint(v2_bp, url_prefix="/v2") app.register_blueprint(v2_bp, url_prefix="/v2")
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
if not features.SECURITY_SCANNER: if not features.SECURITY_SCANNER:
logger.debug("Security scanner disabled; skipping SecurityWorker") logger.debug("Security scanner disabled; skipping SecurityWorker")
while True: while True:

View File

@ -202,6 +202,11 @@ if __name__ == "__main__":
has_local_storage = False has_local_storage = False
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
if features.STORAGE_REPLICATION: if features.STORAGE_REPLICATION:
for storage_type, _ in list(app.config.get("DISTRIBUTED_STORAGE_CONFIG", {}).values()): for storage_type, _ in list(app.config.get("DISTRIBUTED_STORAGE_CONFIG", {}).values()):
if storage_type == "LocalStorage": if storage_type == "LocalStorage":

View File

@ -47,6 +47,11 @@ def create_gunicorn_worker():
def main(): def main():
logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False) logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False)
if app.config.get("ACCOUNT_RECOVERY_MODE", False):
logger.debug("Quay running in account recovery mode")
while True:
time.sleep(100000)
if not features.TEAM_SYNCING or not authentication.federated_service: if not features.TEAM_SYNCING or not authentication.federated_service:
logger.debug("Team syncing is disabled; sleeping") logger.debug("Team syncing is disabled; sleeping")
while True: while True: