113 lines
4.5 KiB
Python
113 lines
4.5 KiB
Python
|
import inspect
|
||
|
import logging
|
||
|
import hashlib
|
||
|
|
||
|
from data.database import DerivedStorageForImage, TagManifest, Manifest, Image
|
||
|
from data.registry_model.registry_oci_model import back_compat_oci_model, oci_model
|
||
|
from data.registry_model.registry_pre_oci_model import pre_oci_model
|
||
|
from data.registry_model.datatypes import LegacyImage, Manifest as ManifestDataType
|
||
|
|
||
|
|
||
|
logger = logging.getLogger(__name__)
|
||
|
|
||
|
|
||
|
class SplitModel(object):
|
||
|
def __init__(self, oci_model_proportion, oci_namespace_whitelist, v22_namespace_whitelist,
|
||
|
oci_only_mode):
|
||
|
self.v22_namespace_whitelist = set(v22_namespace_whitelist)
|
||
|
|
||
|
self.oci_namespace_whitelist = set(oci_namespace_whitelist)
|
||
|
self.oci_namespace_whitelist.update(v22_namespace_whitelist)
|
||
|
|
||
|
self.oci_model_proportion = oci_model_proportion
|
||
|
self.oci_only_mode = oci_only_mode
|
||
|
|
||
|
def supports_schema2(self, namespace_name):
|
||
|
""" Returns whether the implementation of the data interface supports schema 2 format
|
||
|
manifests. """
|
||
|
return namespace_name in self.v22_namespace_whitelist
|
||
|
|
||
|
def _namespace_from_kwargs(self, args_dict):
|
||
|
if 'namespace_name' in args_dict:
|
||
|
return args_dict['namespace_name']
|
||
|
|
||
|
if 'repository_ref' in args_dict:
|
||
|
return args_dict['repository_ref'].namespace_name
|
||
|
|
||
|
if 'tag' in args_dict:
|
||
|
return args_dict['tag'].repository.namespace_name
|
||
|
|
||
|
if 'manifest' in args_dict:
|
||
|
manifest = args_dict['manifest']
|
||
|
if manifest._is_tag_manifest:
|
||
|
return TagManifest.get(id=manifest._db_id).tag.repository.namespace_user.username
|
||
|
else:
|
||
|
return Manifest.get(id=manifest._db_id).repository.namespace_user.username
|
||
|
|
||
|
if 'manifest_or_legacy_image' in args_dict:
|
||
|
manifest_or_legacy_image = args_dict['manifest_or_legacy_image']
|
||
|
if isinstance(manifest_or_legacy_image, LegacyImage):
|
||
|
return Image.get(id=manifest_or_legacy_image._db_id).repository.namespace_user.username
|
||
|
else:
|
||
|
manifest = manifest_or_legacy_image
|
||
|
if manifest._is_tag_manifest:
|
||
|
return TagManifest.get(id=manifest._db_id).tag.repository.namespace_user.username
|
||
|
else:
|
||
|
return Manifest.get(id=manifest._db_id).repository.namespace_user.username
|
||
|
|
||
|
if 'derived_image' in args_dict:
|
||
|
return (DerivedStorageForImage
|
||
|
.get(id=args_dict['derived_image']._db_id)
|
||
|
.source_image
|
||
|
.repository
|
||
|
.namespace_user
|
||
|
.username)
|
||
|
|
||
|
if 'blob' in args_dict:
|
||
|
return '' # Blob functions are shared, so no need to do anything.
|
||
|
|
||
|
if 'blob_upload' in args_dict:
|
||
|
return '' # Blob functions are shared, so no need to do anything.
|
||
|
|
||
|
raise Exception('Unknown namespace for dict `%s`' % args_dict)
|
||
|
|
||
|
def __getattr__(self, attr):
|
||
|
def method(*args, **kwargs):
|
||
|
if self.oci_model_proportion >= 1.0:
|
||
|
if self.oci_only_mode:
|
||
|
logger.debug('Calling method `%s` under full OCI data model for all namespaces', attr)
|
||
|
return getattr(oci_model, attr)(*args, **kwargs)
|
||
|
else:
|
||
|
logger.debug('Calling method `%s` under compat OCI data model for all namespaces', attr)
|
||
|
return getattr(back_compat_oci_model, attr)(*args, **kwargs)
|
||
|
|
||
|
argnames = inspect.getargspec(getattr(back_compat_oci_model, attr))[0]
|
||
|
if not argnames and isinstance(args[0], ManifestDataType):
|
||
|
args_dict = dict(manifest=args[0])
|
||
|
else:
|
||
|
args_dict = {argnames[index + 1]: value for index, value in enumerate(args)}
|
||
|
|
||
|
if attr in ['yield_tags_for_vulnerability_notification', 'get_most_recent_tag_lifetime_start']:
|
||
|
use_oci = self.oci_model_proportion >= 1.0
|
||
|
namespace_name = '(implicit for ' + attr + ')'
|
||
|
else:
|
||
|
namespace_name = self._namespace_from_kwargs(args_dict)
|
||
|
use_oci = namespace_name in self.oci_namespace_whitelist
|
||
|
|
||
|
if not use_oci and self.oci_model_proportion:
|
||
|
# Hash the namespace name and see if it falls into the proportion bucket.
|
||
|
bucket = (int(hashlib.md5(namespace_name).hexdigest(), 16) % 100)
|
||
|
if bucket <= int(self.oci_model_proportion * 100):
|
||
|
logger.debug('Enabling OCI for namespace `%s` in proportional bucket',
|
||
|
namespace_name)
|
||
|
use_oci = True
|
||
|
|
||
|
if use_oci:
|
||
|
logger.debug('Calling method `%s` under OCI data model for namespace `%s`',
|
||
|
attr, namespace_name)
|
||
|
return getattr(back_compat_oci_model, attr)(*args, **kwargs)
|
||
|
else:
|
||
|
return getattr(pre_oci_model, attr)(*args, **kwargs)
|
||
|
|
||
|
return method
|