parent
5211c407ff
commit
8fe29c5b89
12 changed files with 320 additions and 60 deletions
|
@ -75,8 +75,12 @@ class BaseProvider(object):
|
|||
""" Returns whether the file with the given name exists under the config override volume. """
|
||||
raise NotImplementedError
|
||||
|
||||
def get_volume_file(self, filename, mode='r'):
|
||||
""" Returns a Python file referring to the given name under the config override volumne. """
|
||||
def get_volume_file(self, filename):
|
||||
""" Returns a Python file referring to the given name under the config override volume. """
|
||||
raise NotImplementedError
|
||||
|
||||
def write_volume_file(self, filename, contents):
|
||||
""" Writes the given contents to the config override volumne, with the given filename. """
|
||||
raise NotImplementedError
|
||||
|
||||
def save_volume_file(self, filename, flask_file):
|
||||
|
@ -91,6 +95,14 @@ class BaseProvider(object):
|
|||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _get_license_file(self):
|
||||
""" Returns the contents of the license file. """
|
||||
try:
|
||||
return self.get_volume_file(LICENSE_FILENAME)
|
||||
except IOError:
|
||||
msg = 'Could not open license file. Please make sure it is in your config volume.'
|
||||
raise LicenseError(msg)
|
||||
|
||||
def validate_license(self, config):
|
||||
""" Validates that the configuration matches the license file (if any). """
|
||||
if not config.get('SETUP_COMPLETE', False):
|
||||
|
@ -102,11 +114,10 @@ class BaseProvider(object):
|
|||
self.license = decode_license(license_file_contents)
|
||||
self.license.validate(config)
|
||||
|
||||
def _get_license_file(self):
|
||||
""" Returns the contents of the license file. """
|
||||
try:
|
||||
return self.get_volume_file(LICENSE_FILENAME)
|
||||
except IOError:
|
||||
msg = 'Could not open license file. Please make sure it is in your config volume.'
|
||||
raise LicenseError(msg)
|
||||
def save_license(self, license_file_contents):
|
||||
""" Saves the given contents as the license file. """
|
||||
self.write_volume_file(LICENSE_FILENAME, license_file_contents)
|
||||
|
||||
def has_license_file(self):
|
||||
""" Returns true if a license file was found in the config directory. """
|
||||
return self.volume_file_exists(LICENSE_FILENAME)
|
||||
|
|
|
@ -49,8 +49,15 @@ class FileConfigProvider(BaseProvider):
|
|||
def volume_file_exists(self, filename):
|
||||
return os.path.exists(os.path.join(self.config_volume, filename))
|
||||
|
||||
def get_volume_file(self, filename, mode='r'):
|
||||
return open(os.path.join(self.config_volume, filename), mode)
|
||||
def get_volume_file(self, filename):
|
||||
return open(os.path.join(self.config_volume, filename))
|
||||
|
||||
def write_volume_file(self, filename, contents):
|
||||
filepath = os.path.join(self.config_volume, filename)
|
||||
with open(filepath, mode='w') as f:
|
||||
f.write(contents)
|
||||
|
||||
return filepath
|
||||
|
||||
def save_volume_file(self, filename, flask_file):
|
||||
filepath = os.path.join(self.config_volume, filename)
|
||||
|
|
|
@ -47,6 +47,14 @@ class KubernetesConfigProvider(FileConfigProvider):
|
|||
self._update_secret_file(self.yaml_filename, get_yaml(config_obj))
|
||||
super(KubernetesConfigProvider, self).save_config(config_obj)
|
||||
|
||||
def write_volume_file(self, filename, contents):
|
||||
super(KubernetesConfigProvider, self).write_volume_file(filename, contents)
|
||||
|
||||
try:
|
||||
self._update_secret_file(filename, contents)
|
||||
except IOError as ioe:
|
||||
raise CannotWriteConfigException(str(ioe))
|
||||
|
||||
def save_volume_file(self, filename, flask_file):
|
||||
filepath = super(KubernetesConfigProvider, self).save_volume_file(filename, flask_file)
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ from cryptography.hazmat.backends import default_backend
|
|||
from cryptography.hazmat.primitives.serialization import load_pem_public_key
|
||||
|
||||
import jwt
|
||||
import json
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -44,6 +45,19 @@ class License(object):
|
|||
def __init__(self, decoded):
|
||||
self.decoded = decoded
|
||||
|
||||
@property
|
||||
def subscription(self):
|
||||
""" Returns the Quay Enterprise subscription, if any. """
|
||||
for sub in self.decoded.get('subscriptions', {}).values():
|
||||
if sub.get('productName') == LICENSE_PRODUCT_NAME:
|
||||
return sub
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def is_expired(self):
|
||||
return self._get_expired(datetime.now())
|
||||
|
||||
def validate(self, config):
|
||||
""" Validates the license and all its entitlements against the given config. """
|
||||
# Check that the license has not expired.
|
||||
|
@ -58,10 +72,6 @@ class License(object):
|
|||
max_regions)
|
||||
raise LicenseValidationError(msg)
|
||||
|
||||
@property
|
||||
def is_expired(self):
|
||||
return self._get_expired(datetime.now())
|
||||
|
||||
def _get_expired(self, compare_date):
|
||||
# Check if the license overall has expired.
|
||||
expiration_date = _get_date(self.decoded, 'expirationDate')
|
||||
|
@ -70,37 +80,37 @@ class License(object):
|
|||
return True
|
||||
|
||||
# Check for any QE subscriptions.
|
||||
for sub in self.decoded.get('subscriptions', []):
|
||||
if sub.get('productName') != LICENSE_PRODUCT_NAME:
|
||||
continue
|
||||
sub = self.subscription
|
||||
if sub is None:
|
||||
return True
|
||||
|
||||
# Check for a trial-only license.
|
||||
if sub.get('trialOnly', False):
|
||||
trial_end_date = _get_date(sub, 'trialEnd')
|
||||
logger.debug('Trial-only license expires on %s', trial_end_date)
|
||||
return trial_end_date <= (compare_date - TRIAL_GRACE_PERIOD)
|
||||
# Check for a trial-only license.
|
||||
if sub.get('trialOnly', False):
|
||||
trial_end_date = _get_date(sub, 'trialEnd')
|
||||
logger.debug('Trial-only license expires on %s', trial_end_date)
|
||||
return trial_end_date <= (compare_date - TRIAL_GRACE_PERIOD)
|
||||
|
||||
# Check for a normal license that is in trial.
|
||||
service_end_date = _get_date(sub, 'serviceEnd')
|
||||
if sub.get('inTrial', False):
|
||||
# If the subscription is in a trial, but not a trial only
|
||||
# subscription, give 7 days after trial end to update license
|
||||
# to one which has been paid (they've put in a credit card and it
|
||||
# might auto convert, so we could assume it will auto-renew)
|
||||
logger.debug('In-trial license expires on %s', service_end_date)
|
||||
return service_end_date <= (compare_date - TRIAL_GRACE_PERIOD)
|
||||
# Check for a normal license that is in trial.
|
||||
service_end_date = _get_date(sub, 'serviceEnd')
|
||||
if sub.get('inTrial', False):
|
||||
# If the subscription is in a trial, but not a trial only
|
||||
# subscription, give 7 days after trial end to update license
|
||||
# to one which has been paid (they've put in a credit card and it
|
||||
# might auto convert, so we could assume it will auto-renew)
|
||||
logger.debug('In-trial license expires on %s', service_end_date)
|
||||
return service_end_date <= (compare_date - TRIAL_GRACE_PERIOD)
|
||||
|
||||
# Otherwise, check the service expiration.
|
||||
duration_period = sub.get('durationPeriod', 'monthly')
|
||||
# Otherwise, check the service expiration.
|
||||
duration_period = sub.get('durationPeriod', 'months')
|
||||
|
||||
# If the subscription is monthly, give 3 months grace period
|
||||
if duration_period == "monthly":
|
||||
logger.debug('Monthly license expires on %s', service_end_date)
|
||||
return service_end_date <= (compare_date - MONTHLY_GRACE_PERIOD)
|
||||
# If the subscription is monthly, give 3 months grace period
|
||||
if duration_period == "months":
|
||||
logger.debug('Monthly license expires on %s', service_end_date)
|
||||
return service_end_date <= (compare_date - MONTHLY_GRACE_PERIOD)
|
||||
|
||||
if duration_period == "years":
|
||||
logger.debug('Yearly license expires on %s', service_end_date)
|
||||
return service_end_date <= (compare_date - YEARLY_GRACE_PERIOD)
|
||||
if duration_period == "years":
|
||||
logger.debug('Yearly license expires on %s', service_end_date)
|
||||
return service_end_date <= (compare_date - YEARLY_GRACE_PERIOD)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -128,9 +138,15 @@ def decode_license(license_contents, public_key_instance=None):
|
|||
""" Decodes the specified license contents, returning the decoded license. """
|
||||
license_public_key = public_key_instance or _PROD_LICENSE_PUBLIC_KEY
|
||||
try:
|
||||
decoded = jwt.decode(license_contents, key=license_public_key)
|
||||
jwt_data = jwt.decode(license_contents, key=license_public_key)
|
||||
except jwt.exceptions.DecodeError as de:
|
||||
logger.exception('Could not decode license file')
|
||||
raise LicenseDecodeError('Could not decode license found: %s' % de.message)
|
||||
|
||||
try:
|
||||
decoded = json.loads(jwt_data.get('license', '{}'))
|
||||
except ValueError as ve:
|
||||
logger.exception('Could not decode license file')
|
||||
raise LicenseDecodeError('Could not decode license found: %s' % ve.message)
|
||||
|
||||
return License(decoded)
|
||||
|
|
|
@ -46,6 +46,9 @@ class TestConfigProvider(BaseProvider):
|
|||
def save_volume_file(self, filename, flask_file):
|
||||
self.files[filename] = ''
|
||||
|
||||
def write_volume_file(self, filename, contents):
|
||||
self.files[filename] = contents
|
||||
|
||||
def get_volume_file(self, filename, mode='r'):
|
||||
if filename in REAL_FILES:
|
||||
return open(filename, mode=mode)
|
||||
|
|
Reference in a new issue