Switch out existing obfuscated license with external module

This creates a new fallback license module called StubLicense that
will be used in the event that the tower_license module is not
installed.

All existing license mechanisms are routed through the get_licenser()
util method
This commit is contained in:
Matthew Jones 2017-07-11 12:01:24 -04:00
parent 7fda3c0658
commit 8486944eaa
13 changed files with 83 additions and 45 deletions

View File

@ -971,6 +971,9 @@ docker-compose-test: docker-auth
docker-compose-build: tower-devel-build tower-isolated-build
tower-devel-build:
rm -rf tools/docker-compose/tower-license
git clone git@github.com:ansible/tower-license.git tools/docker-compose/tower-license
cd tools/docker-compose/tower-license && $(PYTHON) setup.py sdist
docker build -t ansible/tower_devel -f tools/docker-compose/Dockerfile .
docker tag ansible/tower_devel gcr.io/ansible-tower-engineering/tower_devel:$(COMPOSE_TAG)
#docker push gcr.io/ansible-tower-engineering/tower_devel:$(COMPOSE_TAG)

View File

@ -324,9 +324,9 @@ class ApiV1ConfigView(APIView):
extra=dict(actor=request.user.username))
return Response({"error": _("Invalid JSON")}, status=status.HTTP_400_BAD_REQUEST)
try:
from awx.main.task_engine import TaskEnhancer
from awx.main.utils.common import get_licenser
license_data = json.loads(data_actual)
license_data_validated = TaskEnhancer(**license_data).validate_enhancements()
license_data_validated = get_licenser(**license_data).validate()
except Exception:
logger.warning(smart_text(u"Invalid license submitted."),
extra=dict(actor=request.user.username))

View File

@ -8,7 +8,7 @@ from django.utils.translation import ugettext_lazy as _
from rest_framework.exceptions import APIException
# Tower
from awx.main.task_engine import TaskEnhancer
from awx.main.utils.common import get_licenser
__all__ = ['LicenseForbids', 'get_license', 'get_licensed_features',
'feature_enabled', 'feature_exists']
@ -20,7 +20,7 @@ class LicenseForbids(APIException):
def _get_validated_license_data():
return TaskEnhancer().validate_enhancements()
return get_licenser().validate()
def get_license(show_key=False):
@ -42,7 +42,10 @@ def get_licensed_features():
def feature_enabled(name):
"""Return True if the requested feature is enabled, False otherwise."""
return _get_validated_license_data().get('features', {}).get(name, False)
validated_license_data = _get_validated_license_data()
if validated_license_data['license_type'] == 'open':
return True
return validated_license_data.get('features', {}).get(name, False)
def feature_exists(name):

View File

@ -21,8 +21,6 @@ from awx.main.utils import * # noqa
from awx.main.models import * # noqa
from awx.main.models.unified_jobs import ACTIVE_STATES
from awx.main.models.mixins import ResourceMixin
from awx.main.task_engine import TaskEnhancer
from awx.conf.license import LicenseForbids
__all__ = ['get_user_queryset', 'check_user_access', 'check_user_access_with_errors',
'user_accessible_objects', 'consumer_access',
@ -255,7 +253,10 @@ class BaseAccess(object):
return True # User has access to both, permission check passed
def check_license(self, add_host_name=None, feature=None, check_expiration=True):
validation_info = TaskEnhancer().validate_enhancements()
validation_info = get_licenser().validate()
if validation_info['license_type'] == 'open':
return
if ('test' in sys.argv or 'py.test' in sys.argv[0] or 'jenkins' in sys.argv) and not os.environ.get('SKIP_LICENSE_FIXUP_FOR_TEST', ''):
validation_info['free_instances'] = 99999999
validation_info['time_remaining'] = 99999999

View File

@ -22,12 +22,12 @@ from django.utils.encoding import smart_text
# AWX
from awx.main.models import * # noqa
from awx.main.task_engine import TaskEnhancer
from awx.main.utils import (
ignore_inventory_computed_fields,
check_proot_installed,
wrap_args_with_proot,
build_proot_temp_dir
build_proot_temp_dir,
get_licenser
)
from awx.main.utils.mem_inventory import MemInventory, dict_to_mem_data
from awx.main.signals import disable_activity_stream
@ -845,10 +845,12 @@ class Command(NoArgsCommand):
self._create_update_group_hosts()
def check_license(self):
license_info = TaskEnhancer().validate_enhancements()
license_info = get_licenser().validate()
if license_info.get('license_key', 'UNLICENSED') == 'UNLICENSED':
logger.error(LICENSE_NON_EXISTANT_MESSAGE)
raise CommandError('No license found!')
elif license_info['license_type'] == 'open':
return
available_instances = license_info.get('available_instances', 0)
free_instances = license_info.get('free_instances', 0)
time_remaining = license_info.get('time_remaining', 0)

View File

@ -47,10 +47,9 @@ from awx.main.constants import CLOUD_PROVIDERS
from awx.main.models import * # noqa
from awx.main.models.unified_jobs import ACTIVE_STATES
from awx.main.queue import CallbackQueueDispatcher
from awx.main.task_engine import TaskEnhancer
from awx.main.isolated import run, isolated_manager
from awx.main.utils import (get_ansible_version, get_ssh_version, decrypt_field, update_scm_url,
check_proot_installed, build_proot_temp_dir,
check_proot_installed, build_proot_temp_dir, get_licenser,
wrap_args_with_proot, get_system_task_capacity, OutputEventFilter,
parse_yaml_or_json, ignore_inventory_computed_fields, ignore_inventory_group_removal)
from awx.main.utils.reload import restart_local_services, stop_local_services
@ -140,8 +139,8 @@ def run_administrative_checks(self):
logger.warn("Running administrative checks.")
if not settings.TOWER_ADMIN_ALERTS:
return
validation_info = TaskEnhancer().validate_enhancements()
if validation_info.get('instance_count', 0) < 1:
validation_info = get_licenser().validate()
if validation_info['license_type'] != 'open' and validation_info.get('instance_count', 0) < 1:
return
used_percentage = float(validation_info.get('current_instances', 0)) / float(validation_info.get('instance_count', 100))
tower_admin_emails = User.objects.filter(is_superuser=True).values_list('email', flat=True)

View File

@ -29,11 +29,12 @@ from django.utils.encoding import force_text
# AWX
from awx.main.models import * # noqa
from awx.main.task_engine import TaskEnhancer
from awx.main.utils import get_ansible_version
from awx.sso.backends import LDAPSettings
from awx.main.tests.URI import URI # noqa
from tower_license import TowerLicense
TEST_PLAYBOOK = '''- hosts: mygroup
gather_facts: false
tasks:
@ -179,7 +180,7 @@ class BaseTestMixin(MockCommonlySlowTestMixin):
return __name__ + '-generated-' + string + rnd_str
def create_test_license_file(self, instance_count=10000, license_date=int(time.time() + 3600), features={}):
settings.LICENSE = TaskEnhancer(
settings.LICENSE = TowerLicense(
company_name='AWX',
contact_name='AWX Admin',
contact_email='awx@example.com',
@ -187,17 +188,17 @@ class BaseTestMixin(MockCommonlySlowTestMixin):
instance_count=instance_count,
license_type='enterprise',
features=features,
).enhance()
).generate()
def create_basic_license_file(self, instance_count=100, license_date=int(time.time() + 3600)):
settings.LICENSE = TaskEnhancer(
settings.LICENSE = TowerLicense(
company_name='AWX',
contact_name='AWX Admin',
contact_email='awx@example.com',
license_date=license_date,
instance_count=instance_count,
license_type='basic',
).enhance()
).generate()
def create_expired_license_file(self, instance_count=1000, grace_period=False):
license_date = time.time() - 1

View File

@ -101,12 +101,12 @@ def job_template_with_survey_passwords_unit(job_template_with_survey_passwords_f
@pytest.fixture
def enterprise_license():
from awx.main.task_engine import TaskEnhancer
return TaskEnhancer(
from tower_license import TowerLicense
return TowerLicense(
company_name='AWX',
contact_name='AWX Admin',
contact_email='awx@example.com',
license_date=int(time.time() + 3600),
instance_count=10000,
license_type='enterprise',
).enhance()
).generate()

View File

@ -6,19 +6,20 @@ import pytest
from datetime import datetime
from awx.main.models import Host
from awx.main.task_engine import TaskEnhancer
from awx.main.utils import get_licenser, StubLicense
from tower_license import TowerLicense
@pytest.mark.django_db
def test_license_writer(inventory, admin):
task_enhancer = TaskEnhancer(
license_actual = TowerLicense(
company_name='acmecorp',
contact_name='Michael DeHaan',
contact_email='michael@ansibleworks.com',
license_date=25000, # seconds since epoch
instance_count=500)
data = task_enhancer.enhance()
data = license_actual.generate()
Host.objects.bulk_create(
[
@ -38,7 +39,7 @@ def test_license_writer(inventory, admin):
assert data['license_date'] == 25000
assert data['license_key'] == "11bae31f31c6a6cdcb483a278cdbe98bd8ac5761acd7163a50090b0f098b3a13"
vdata = task_enhancer.validate_enhancements()
vdata = license_actual.validate()
assert vdata['available_instances'] == 500
assert vdata['current_instances'] == 12
@ -52,43 +53,51 @@ def test_license_writer(inventory, admin):
assert vdata['subscription_name']
def test_stub_license():
license_actual = StubLicense()
assert license_actual['license_key'] == 'OPEN'
assert license_actual['valid_key']
assert license_actual['compliant']
assert license_actual['license_type'] == 'open'
@pytest.mark.django_db
def test_expired_licenses():
task_enhancer = TaskEnhancer(
license_actual = TowerLicense(
company_name='Tower',
contact_name='Tower Admin',
contact_email='tower@ansible.com',
license_date=int(time.time() - 3600),
instance_count=100,
trial=True)
task_enhancer.enhance()
vdata = task_enhancer.validate_enhancements()
license_actual.generate()
vdata = license_actual.validate()
assert vdata['compliant'] is False
assert vdata['grace_period_remaining'] < 0
task_enhancer = TaskEnhancer(
license_actual = TowerLicense(
company_name='Tower',
contact_name='Tower Admin',
contact_email='tower@ansible.com',
license_date=int(time.time() - 2592001),
instance_count=100,
trial=False)
task_enhancer.enhance()
vdata = task_enhancer.validate_enhancements()
license_actual.generate()
vdata = license_actual.validate()
assert vdata['compliant'] is False
assert vdata['grace_period_remaining'] < 0
task_enhancer = TaskEnhancer(
license_actual = TowerLicense(
company_name='Tower',
contact_name='Tower Admin',
contact_email='tower@ansible.com',
license_date=int(time.time() - 3600),
instance_count=100,
trial=False)
task_enhancer.enhance()
vdata = task_enhancer.validate_enhancements()
license_actual.generate()
vdata = license_actual.validate()
assert vdata['compliant'] is False
assert vdata['grace_period_remaining'] > 0
@ -96,9 +105,9 @@ def test_expired_licenses():
@pytest.mark.django_db
def test_cloudforms_license(mocker):
with mocker.patch('awx.main.task_engine.TaskEnhancer._check_cloudforms_subscription', return_value=True):
task_enhancer = TaskEnhancer()
vdata = task_enhancer.validate_enhancements()
with mocker.patch('tower_license.TowerLicense._check_cloudforms_subscription', return_value=True):
license_actual = TowerLicense()
vdata = license_actual.validate()
assert vdata['compliant'] is True
assert vdata['subscription_name'] == "Red Hat CloudForms License"
assert vdata['available_instances'] == 9999999

View File

@ -268,7 +268,7 @@ class TestCheckLicense:
def exists(self):
return host_exists
mocker.patch('awx.main.tasks.TaskEnhancer.validate_enhancements', return_value={'free_instances': free_instances, 'available_instances': available_instances, 'date_warning': True})
mocker.patch('tower_license.TowerLicense.validate', return_value={'free_instances': free_instances, 'available_instances': available_instances, 'date_warning': True})
mock_filter = MockFilter()
mocker.patch('awx.main.models.Host.objects.filter', return_value=mock_filter)

View File

@ -29,9 +29,10 @@ from awx.main.models import (
)
from awx.main import tasks
from awx.main.task_engine import TaskEnhancer
from awx.main.utils import encrypt_field
from tower_license import TowerLicense
@contextmanager
def apply_patches(_patches):
@ -83,9 +84,9 @@ def test_run_admin_checks_usage(mocker, current_instances, call_count):
patches = list()
patches.append(mocker.patch('awx.main.tasks.User'))
mock_te = mocker.Mock(spec=TaskEnhancer)
mock_te.validate_enhancements.return_value = {'instance_count': 100, 'current_instances': current_instances, 'date_warning': True}
patches.append(mocker.patch('awx.main.tasks.TaskEnhancer', return_value=mock_te))
mock_te = mocker.Mock(spec=TowerLicense)
mock_te.validate.return_value = {'instance_count': 100, 'current_instances': current_instances, 'date_warning': True}
patches.append(mocker.patch('tower_license.TowerLicense', return_value=mock_te))
mock_sm = mocker.Mock()
patches.append(mocker.patch('awx.main.tasks.send_mail', wraps=mock_sm))

View File

@ -33,7 +33,7 @@ from django.apps import apps
logger = logging.getLogger('awx.main.utils')
__all__ = ['get_object_or_400', 'get_object_or_403', 'camelcase_to_underscore', 'memoize',
'get_ansible_version', 'get_ssh_version', 'get_awx_version', 'update_scm_url',
'get_ansible_version', 'get_ssh_version', 'get_licenser', 'get_awx_version', 'update_scm_url',
'get_type_for_model', 'get_model_for_type', 'copy_model_by_class',
'copy_m2m_relationships' ,'cache_list_capabilities', 'to_python_boolean',
'ignore_inventory_computed_fields', 'ignore_inventory_group_removal',
@ -161,6 +161,23 @@ def get_awx_version():
return __version__
class StubLicense(object):
def validate(self):
return dict(license_key='OPEN',
valid_key=True,
compliant=True,
license_type='open')
def get_licenser(*args, **kwargs):
try:
from tower_license import TowerLicense
return TowerLicense(*args, **kwargs)
except ImportError:
return StubLicense(*args, **kwargs)
def update_scm_url(scm_type, url, username=True, password=True,
check_special_cases=True, scp_format=False):
'''

View File

@ -2,6 +2,7 @@ FROM centos:7
ADD Makefile /tmp/Makefile
RUN mkdir /tmp/requirements
COPY tools/docker-compose/tower-license/dist/tower-license-0.1.tar.gz /tmp/tower-license.tar.gz
ADD requirements/requirements.txt \
requirements/requirements_git.txt \
requirements/requirements_ansible.txt \
@ -33,6 +34,7 @@ RUN openssl req -nodes -newkey rsa:2048 -keyout /etc/nginx/nginx.key -out /etc/n
RUN openssl x509 -req -days 365 -in /etc/nginx/nginx.csr -signkey /etc/nginx/nginx.key -out /etc/nginx/nginx.crt
WORKDIR /tmp
RUN SWIG_FEATURES="-cpperraswarn -includeall -D__`uname -m`__ -I/usr/include/openssl" VENV_BASE="/venv" make requirements_dev
RUN /venv/tower/bin/pip install /tmp/tower-license.tar.gz
RUN localedef -c -i en_US -f UTF-8 en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en