From 193382c7f5645b12b4dacc48d94d49e8ac5fbd53 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Tue, 13 Mar 2018 13:05:15 -0400 Subject: [PATCH 1/9] integrated AWX tests --- .gitignore | 4 + .travis.yml | 6 +- install_awx.sh | 9 + install_awx_req.py | 34 + pytest.ini | 4 + requirements_dev.txt | 3 +- tests_awx/CLI_settings.py | 1104 ++++++++++++++++++++++++++++++++ tests_awx/conftest.py | 99 +++ tests_awx/test_organization.py | 9 + 9 files changed, 1269 insertions(+), 3 deletions(-) create mode 100755 install_awx.sh create mode 100644 install_awx_req.py create mode 100644 pytest.ini create mode 100644 tests_awx/CLI_settings.py create mode 100644 tests_awx/conftest.py create mode 100644 tests_awx/test_organization.py diff --git a/.gitignore b/.gitignore index a980a5fb..f2798d80 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,7 @@ setup_v1.py tower_cli_v2/ bin/tower-cli-v2 setup_v2.py + +# for AWX install +/awx +.cache/ diff --git a/.travis.yml b/.travis.yml index e05ccaf3..d058a55a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,11 +10,13 @@ python: matrix: allow_failures: - python: nightly +before_install: + - ./install_awx.sh install: - pip install -r requirements_dev.txt -before_script: - flake8 . script: + - flake8 tower-cli/ tests/ - tox + - py.test tests_awx/ after_success: coveralls diff --git a/install_awx.sh b/install_awx.sh new file mode 100755 index 00000000..7be4593b --- /dev/null +++ b/install_awx.sh @@ -0,0 +1,9 @@ +rm -rf awx/ +git clone https://github.com/ansible/awx.git awx --depth=1 +cp tests_awx/CLI_settings.py awx/awx/settings/CLI_settings.py +python install_awx_req.py +cd awx +python setup.py install +cd .. +# have to add awx dir to path +[[ ":$PYTHONPATH:" != *":$PWD/awx:"* ]] && PYTHONPATH="${PYTHONPATH}:$PWD/awx" diff --git a/install_awx_req.py b/install_awx_req.py new file mode 100644 index 00000000..eafa243a --- /dev/null +++ b/install_awx_req.py @@ -0,0 +1,34 @@ +from subprocess import call + +files = ['requirements.txt', 'requirements_dev.txt'] + + +seen = set([]) +failed = set([]) + + +for file_name in files: + rel_path = 'awx/requirements/{}'.format(file_name) + with open(rel_path, 'r') as f: + data = f.read() + for line in data.split('\n'): + if not line or line.startswith('#') or not line.split('#'): + continue + target = line.split('#')[0].strip() + pkg = target.split('=')[0] + # same package listed in multiple files + if pkg in seen: + print('Skipping second listing of ' + str(pkg)) + continue + # exclusions + if pkg in ['pip', 'setuptools']: + print('Passing over {}, in exclusions list'.format(target)) + continue + seen.add(pkg) + r = call("pip install " + target, shell=True) + if r: + failed.add(target) + +if failed: + print 'tower-cli AWX integration failed to install packages \n' + print ' - \n'.join(failed) diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..0e332705 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +DJANGO_SETTINGS_MODULE = awx.settings.CLI_settings +python_files = *.py +addopts = --reuse-db --nomigrations --tb=native \ No newline at end of file diff --git a/requirements_dev.txt b/requirements_dev.txt index c952b770..e6f5620b 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,3 +1,4 @@ flake8 tox-travis -coveralls \ No newline at end of file +coveralls +pytest \ No newline at end of file diff --git a/tests_awx/CLI_settings.py b/tests_awx/CLI_settings.py new file mode 100644 index 00000000..6fafcbca --- /dev/null +++ b/tests_awx/CLI_settings.py @@ -0,0 +1,1104 @@ + + +# Copyright (c) 2015 Ansible, Inc. +# All Rights Reserved. + +import os +import re # noqa +import sys +import ldap +import djcelery +from datetime import timedelta + +from kombu import Queue, Exchange +from kombu.common import Broadcast + +# global settings +from django.conf import global_settings +# ugettext lazy +# from django.utils.translation import ugettext_lazy as _ + +# Python +import copy + +# Centos-7 doesn't include the svg mime type +# /usr/lib64/python/mimetypes.py +import mimetypes + + +# Update this module's local settings from the global settings module. +this_module = sys.modules[__name__] +for setting in dir(global_settings): + if setting == setting.upper(): + setattr(this_module, setting, getattr(global_settings, setting)) + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(__file__)) + + +def is_testing(argv=None): + import sys + '''Return True if running django or py.test unit tests.''' + argv = sys.argv if argv is None else argv + if len(argv) >= 1 and ('py.test' in argv[0] or 'py/test.py' in argv[0]): + return True + elif len(argv) >= 2 and argv[1] == 'test': + return True + return False + + +def IS_TESTING(argv=None): + return is_testing(argv) + + +DEBUG = True +SQL_DEBUG = DEBUG + +ADMINS = ( + # ('Your Name', 'your_email@domain.com'), +) + +MANAGERS = ADMINS + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'awx.sqlite3'), + 'ATOMIC_REQUESTS': True, + 'TEST': { + # Test database cannot be :memory: for celery/inventory tests. + 'NAME': os.path.join(BASE_DIR, 'awx_test.sqlite3'), + }, + } +} + +# Internationalization +# https://docs.djangoproject.com/en/dev/topics/i18n/ +# +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# On Unix systems, a value of None will cause Django to use the same +# timezone as the operating system. +# If running in a Windows environment this must be set to the same as your +# system time zone. +TIME_ZONE = 'UTC' + +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = 'en-us' + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + +# If you set this to False, Django will not format dates, numbers and +# calendars according to the current locale +USE_L10N = True + +USE_TZ = True + +STATICFILES_DIRS = ( + os.path.join(BASE_DIR, 'ui', 'static'), + os.path.join(BASE_DIR, 'static'), +) + +# Absolute filesystem path to the directory where static file are collected via +# the collectstatic command. +STATIC_ROOT = os.path.join(BASE_DIR, 'public', 'static') + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/dev/howto/static-files/ +STATIC_URL = '/static/' + +# Absolute filesystem path to the directory that will hold user-uploaded files. +# Example: "/home/media/media.lawrence.com/" +MEDIA_ROOT = os.path.join(BASE_DIR, 'public', 'media') + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash if there is a path component (optional in other cases). +# Examples: "http://media.lawrence.com", "http://example.com/media/" +MEDIA_URL = '/media/' + +LOGIN_URL = '/api/login/' + +# Absolute filesystem path to the directory to host projects (with playbooks). +# This directory should not be web-accessible. +PROJECTS_ROOT = os.path.join(BASE_DIR, 'projects') + +# Absolute filesystem path to the directory for job status stdout (default for +# development and tests, default for production defined in production.py). This +# directory should not be web-accessible +JOBOUTPUT_ROOT = os.path.join(BASE_DIR, 'job_output') + +# Absolute filesystem path to the directory to store logs +LOG_ROOT = os.path.join(BASE_DIR) + +# The heartbeat file for the tower scheduler +SCHEDULE_METADATA_LOCATION = os.path.join(BASE_DIR, '.tower_cycle') + +# Django gettext files path: locale//LC_MESSAGES/django.po, django.mo +LOCALE_PATHS = ( + os.path.join(BASE_DIR, 'locale'), +) + +# Graph of resources that can have named-url +NAMED_URL_GRAPH = {} + +# Maximum number of the same job that can be waiting to run when launching from scheduler +# Note: This setting may be overridden by database settings. +SCHEDULE_MAX_JOBS = 10 + +SITE_ID = 1 + +# Make this unique, and don't share it with anybody. +SECRET_KEY = 'p7z7g1ql4%6+(6nlebb6hdk7sd^&fnjpal308%n%+p^_e6vo1y' + +# Hosts/domain names that are valid for this site; required if DEBUG is False +# See https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts +ALLOWED_HOSTS = [] + +# HTTP headers and meta keys to search to determine remote host name or IP. Add +# additional items to this list, such as "HTTP_X_FORWARDED_FOR", if behind a +# reverse proxy. +REMOTE_HOST_HEADERS = ['REMOTE_ADDR', 'REMOTE_HOST'] + +# If Tower is behind a reverse proxy/load balancer, use this setting to +# whitelist the proxy IP addresses from which Tower should trust custom +# REMOTE_HOST_HEADERS header values +# REMOTE_HOST_HEADERS = ['HTTP_X_FORWARDED_FOR', ''REMOTE_ADDR', 'REMOTE_HOST'] +# PROXY_IP_WHITELIST = ['10.0.1.100', '10.0.1.101'] +# If this setting is an empty list (the default), the headers specified by +# REMOTE_HOST_HEADERS will be trusted unconditionally') +PROXY_IP_WHITELIST = [] + +# Note: This setting may be overridden by database settings. +STDOUT_MAX_BYTES_DISPLAY = 1048576 + +# Returned in the header on event api lists as a recommendation to the UI +# on how many events to display before truncating/hiding +MAX_UI_JOB_EVENTS = 4000 + +# The maximum size of the ansible callback event's res data structure +# beyond this limit and the value will be removed +MAX_EVENT_RES_DATA = 700000 + +# Note: This setting may be overridden by database settings. +EVENT_STDOUT_MAX_BYTES_DISPLAY = 1024 + +# The amount of time before a stdout file is expired and removed locally +# Note that this can be recreated if the stdout is downloaded +LOCAL_STDOUT_EXPIRE_TIME = 2592000 + +# The number of processes spawned by the callback receiver to process job +# events into the database +JOB_EVENT_WORKERS = 4 + +# The maximum size of the job event worker queue before requests are blocked +JOB_EVENT_MAX_QUEUE_SIZE = 10000 + +# Disallow sending session cookies over insecure connections +SESSION_COOKIE_SECURE = True + +# Seconds before sessions expire. +# Note: This setting may be overridden by database settings. +SESSION_COOKIE_AGE = 1209600 + +# Maximum number of per-user valid, concurrent sessions. +# -1 is unlimited +# Note: This setting may be overridden by database settings. +SESSIONS_PER_USER = -1 + +# Disallow sending csrf cookies over insecure connections +CSRF_COOKIE_SECURE = True + +# Limit CSRF cookies to browser sessions +CSRF_COOKIE_AGE = None + +TEMPLATES = [ + { + 'NAME': 'default', + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'OPTIONS': { + 'debug': DEBUG, + 'context_processors': [# NOQA + 'django.contrib.auth.context_processors.auth', + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.template.context_processors.i18n', + 'django.template.context_processors.media', + 'django.template.context_processors.static', + 'django.template.context_processors.tz', + 'django.contrib.messages.context_processors.messages', + 'awx.ui.context_processors.settings', + 'awx.ui.context_processors.version', + 'social_django.context_processors.backends', + 'social_django.context_processors.login_redirect', + ], + 'loaders': [( + 'django.template.loaders.cached.Loader', + ('django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader',), + )], + 'builtins': ['awx.main.templatetags.swagger'], + }, + 'DIRS': [ + os.path.join(BASE_DIR, 'templates'), + ], + }, +] + +MIDDLEWARE_CLASSES = ( # NOQA + 'awx.main.middleware.MigrationRanCheckMiddleware', + 'awx.main.middleware.TimingMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.locale.LocaleMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'awx.main.middleware.ActivityStreamMiddleware', + 'awx.sso.middleware.SocialAuthMiddleware', + 'crum.CurrentRequestUserMiddleware', + 'awx.main.middleware.URLModificationMiddleware', +) + + +ROOT_URLCONF = 'awx.urls' + +WSGI_APPLICATION = 'awx.wsgi.application' + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.messages', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.staticfiles', + 'oauth2_provider', + 'rest_framework', + 'django_extensions', + 'djcelery', + 'channels', + 'polymorphic', + 'taggit', + 'social_django', + 'awx.conf', + 'awx.main', + 'awx.api', + 'awx.ui', + 'awx.sso', + 'solo', +) + +INTERNAL_IPS = ('127.0.0.1',) + +MAX_PAGE_SIZE = 200 +REST_FRAMEWORK = { + 'DEFAULT_PAGINATION_CLASS': 'awx.api.pagination.Pagination', + 'PAGE_SIZE': 25, + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'awx.api.authentication.LoggedOAuth2Authentication', + 'awx.api.authentication.SessionAuthentication', + 'awx.api.authentication.LoggedBasicAuthentication', + ), + 'DEFAULT_PERMISSION_CLASSES': ( + 'awx.api.permissions.ModelAccessPermission', + ), + 'DEFAULT_FILTER_BACKENDS': ( + 'awx.api.filters.TypeFilterBackend', + 'awx.api.filters.FieldLookupBackend', + 'rest_framework.filters.SearchFilter', + 'awx.api.filters.OrderByBackend', + ), + 'DEFAULT_PARSER_CLASSES': ( + 'awx.api.parsers.JSONParser', + ), + 'DEFAULT_RENDERER_CLASSES': ( + 'rest_framework.renderers.JSONRenderer', + 'awx.api.renderers.BrowsableAPIRenderer', + ), + 'DEFAULT_METADATA_CLASS': 'awx.api.metadata.Metadata', + 'EXCEPTION_HANDLER': 'awx.api.views.api_exception_handler', + 'VIEW_NAME_FUNCTION': 'awx.api.generics.get_view_name', + 'VIEW_DESCRIPTION_FUNCTION': 'awx.api.generics.get_view_description', + 'NON_FIELD_ERRORS_KEY': '__all__', + 'DEFAULT_VERSION': 'v2', + #'URL_FORMAT_OVERRIDE': None, +} + +AUTHENTICATION_BACKENDS = ( + 'awx.sso.backends.LDAPBackend', + 'awx.sso.backends.LDAPBackend1', + 'awx.sso.backends.LDAPBackend2', + 'awx.sso.backends.LDAPBackend3', + 'awx.sso.backends.LDAPBackend4', + 'awx.sso.backends.LDAPBackend5', + 'awx.sso.backends.RADIUSBackend', + 'awx.sso.backends.TACACSPlusBackend', + 'social_core.backends.google.GoogleOAuth2', + 'social_core.backends.github.GithubOAuth2', + 'social_core.backends.github.GithubOrganizationOAuth2', + 'social_core.backends.github.GithubTeamOAuth2', + 'social_core.backends.azuread.AzureADOAuth2', + 'awx.sso.backends.SAMLAuth', + 'django.contrib.auth.backends.ModelBackend', +) + + +# Django OAuth Toolkit settings +OAUTH2_PROVIDER_APPLICATION_MODEL = 'main.OAuth2Application' +OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = 'main.OAuth2AccessToken' + +OAUTH2_PROVIDER = {'ACCESS_TOKEN_EXPIRE_SECONDS': 31536000000, + 'AUTHORIZATION_CODE_EXPIRE_SECONDS': 600} + +# LDAP server (default to None to skip using LDAP authentication). +# Note: This setting may be overridden by database settings. +AUTH_LDAP_SERVER_URI = None + +# Disable LDAP referrals by default (to prevent certain LDAP queries from +# hanging with AD). +# Note: This setting may be overridden by database settings. +AUTH_LDAP_CONNECTION_OPTIONS = { + ldap.OPT_REFERRALS: 0, + ldap.OPT_NETWORK_TIMEOUT: 30 +} + +# Radius server settings (default to empty string to skip using Radius auth). +# Note: These settings may be overridden by database settings. +RADIUS_SERVER = '' +RADIUS_PORT = 1812 +RADIUS_SECRET = '' + +# TACACS+ settings (default host to empty string to skip using TACACS+ auth). +# Note: These settings may be overridden by database settings. +TACACSPLUS_HOST = '' +TACACSPLUS_PORT = 49 +TACACSPLUS_SECRET = '' +TACACSPLUS_SESSION_TIMEOUT = 5 +TACACSPLUS_AUTH_PROTOCOL = 'ascii' + +# Enable / Disable HTTP Basic Authentication used in the API browser +# Note: Session limits are not enforced when using HTTP Basic Authentication. +# Note: This setting may be overridden by database settings. +AUTH_BASIC_ENABLED = True + +# If set, serve only minified JS for UI. +USE_MINIFIED_JS = False + +# Email address that error messages come from. +SERVER_EMAIL = 'root@localhost' + +# Default email address to use for various automated correspondence from +# the site managers. +DEFAULT_FROM_EMAIL = 'tower@localhost' + +# Subject-line prefix for email messages send with django.core.mail.mail_admins +# or ...mail_managers. Make sure to include the trailing space. +EMAIL_SUBJECT_PREFIX = '[Tower] ' + +# The email backend to use. For possible shortcuts see django.core.mail. +# The default is to use the SMTP backend. +# Third-party backends can be specified by providing a Python path +# to a module that defines an EmailBackend class. +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' + +# Host for sending email. +EMAIL_HOST = 'localhost' + +# Port for sending email. +EMAIL_PORT = 25 + +# Optional SMTP authentication information for EMAIL_HOST. +EMAIL_HOST_USER = '' +EMAIL_HOST_PASSWORD = '' +EMAIL_USE_TLS = False + +# The number of seconds to sleep between status checks for jobs running on isolated nodes +AWX_ISOLATED_CHECK_INTERVAL = 30 + +# The timeout (in seconds) for launching jobs on isolated nodes +AWX_ISOLATED_LAUNCH_TIMEOUT = 600 + +# Ansible connection timeout (in seconds) for communicating with isolated instances +AWX_ISOLATED_CONNECTION_TIMEOUT = 10 + +# The time (in seconds) between the periodic isolated heartbeat status check +AWX_ISOLATED_PERIODIC_CHECK = 600 + +# Memcached django cache configuration +# CACHES = { +# 'default': { +# 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', +# 'LOCATION': '127.0.0.1:11211', +# 'TIMEOUT': 864000, +# 'KEY_PREFIX': 'tower_dev', +# } +# } + +# Use Django-Debug-Toolbar if installed. +try: + import debug_toolbar + INSTALLED_APPS += (debug_toolbar.__name__,) +except ImportError: + pass + +DEBUG_TOOLBAR_CONFIG = { + 'INTERCEPT_REDIRECTS': False, + 'ENABLE_STACKTRACES' : True, +} + +DEVSERVER_DEFAULT_ADDR = '0.0.0.0' +DEVSERVER_DEFAULT_PORT = '8013' + +# Set default ports for live server tests. +os.environ.setdefault('DJANGO_LIVE_TEST_SERVER_ADDRESS', 'localhost:9013-9199') + +djcelery.setup_loader() + +BROKER_POOL_LIMIT = None +BROKER_URL = 'amqp://guest:guest@localhost:5672//' +CELERY_EVENT_QUEUE_TTL = 5 +CELERY_DEFAULT_QUEUE = 'tower' +CELERY_TASK_SERIALIZER = 'json' +CELERY_RESULT_SERIALIZER = 'json' +CELERY_ACCEPT_CONTENT = ['json'] +CELERY_TRACK_STARTED = True +CELERYD_TASK_TIME_LIMIT = None +CELERYD_TASK_SOFT_TIME_LIMIT = None +CELERYD_POOL_RESTARTS = True +CELERY_RESULT_BACKEND = 'djcelery.backends.database:DatabaseBackend' +CELERY_IMPORTS = ('awx.main.scheduler.tasks',) +CELERY_QUEUES = ( + Queue('tower', Exchange('tower'), routing_key='tower'), + Broadcast('tower_broadcast_all') +) +CELERY_ROUTES = {} + +CELERYBEAT_SCHEDULER = 'celery.beat.PersistentScheduler' +CELERYBEAT_MAX_LOOP_INTERVAL = 60 +CELERYBEAT_SCHEDULE = { + 'tower_scheduler': { + 'task': 'awx.main.tasks.awx_periodic_scheduler', + 'schedule': timedelta(seconds=30), + 'options': {'expires': 20,} + }, + 'admin_checks': { + 'task': 'awx.main.tasks.run_administrative_checks', + 'schedule': timedelta(days=30) + }, + 'cluster_heartbeat': { + 'task': 'awx.main.tasks.cluster_node_heartbeat', + 'schedule': timedelta(seconds=60), + 'options': {'expires': 50,} + }, + 'purge_stdout_files': { + 'task': 'awx.main.tasks.purge_old_stdout_files', + 'schedule': timedelta(days=7) + }, + 'task_manager': { + 'task': 'awx.main.scheduler.tasks.run_task_manager', + 'schedule': timedelta(seconds=20), + 'options': {'expires': 20} + }, + 'isolated_heartbeat': { + 'task': 'awx.main.tasks.awx_isolated_heartbeat', + 'schedule': timedelta(seconds=AWX_ISOLATED_PERIODIC_CHECK), + 'options': {'expires': AWX_ISOLATED_PERIODIC_CHECK * 2}, + } +} +AWX_INCONSISTENT_TASK_INTERVAL = 60 * 3 + +# Celery queues that will always be listened to by celery workers +# Note: Broadcast queues have unique, auto-generated names, with the alias +# property value of the original queue name. +AWX_CELERY_QUEUES_STATIC = ['tower_broadcast_all',] + +ASGI_AMQP = { + 'INIT_FUNC': 'awx.prepare_env', + 'MODEL': 'awx.main.models.channels.ChannelGroup', +} + +# Django Caching Configuration +if is_testing(): + CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', + }, + } +else: + CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', + 'LOCATION': 'memcached:11211', + }, + } + +# Social Auth configuration. +SOCIAL_AUTH_STRATEGY = 'social_django.strategy.DjangoStrategy' +SOCIAL_AUTH_STORAGE = 'social_django.models.DjangoStorage' +SOCIAL_AUTH_USER_MODEL = AUTH_USER_MODEL # noqa + +_SOCIAL_AUTH_PIPELINE_BASE = ( + 'social_core.pipeline.social_auth.social_details', + 'social_core.pipeline.social_auth.social_uid', + 'social_core.pipeline.social_auth.auth_allowed', + 'social_core.pipeline.social_auth.social_user', + 'social_core.pipeline.user.get_username', + 'social_core.pipeline.social_auth.associate_by_email', + 'social_core.pipeline.user.create_user', + 'awx.sso.pipeline.check_user_found_or_created', + 'social_core.pipeline.social_auth.associate_user', + 'social_core.pipeline.social_auth.load_extra_data', + 'awx.sso.pipeline.set_is_active_for_new_user', + 'social_core.pipeline.user.user_details', + 'awx.sso.pipeline.prevent_inactive_login', +) +SOCIAL_AUTH_PIPELINE = _SOCIAL_AUTH_PIPELINE_BASE + ( + 'awx.sso.pipeline.update_user_orgs', + 'awx.sso.pipeline.update_user_teams', +) +SOCIAL_AUTH_SAML_PIPELINE = _SOCIAL_AUTH_PIPELINE_BASE + ( + 'awx.sso.pipeline.update_user_orgs_by_saml_attr', + 'awx.sso.pipeline.update_user_teams_by_saml_attr', + 'awx.sso.pipeline.update_user_orgs', + 'awx.sso.pipeline.update_user_teams', +) + +SOCIAL_AUTH_LOGIN_URL = '/' +SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/sso/complete/' +SOCIAL_AUTH_LOGIN_ERROR_URL = '/sso/error/' +SOCIAL_AUTH_INACTIVE_USER_URL = '/sso/inactive/' + +SOCIAL_AUTH_RAISE_EXCEPTIONS = False +SOCIAL_AUTH_USERNAME_IS_FULL_EMAIL = False +#SOCIAL_AUTH_SLUGIFY_USERNAMES = True +SOCIAL_AUTH_CLEAN_USERNAMES = True + +SOCIAL_AUTH_SANITIZE_REDIRECTS = True +SOCIAL_AUTH_REDIRECT_IS_HTTPS = False + +# Note: These settings may be overridden by database settings. +SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = '' +SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = '' +SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = ['profile'] + +SOCIAL_AUTH_GITHUB_KEY = '' +SOCIAL_AUTH_GITHUB_SECRET = '' +SOCIAL_AUTH_GITHUB_SCOPE = ['user:email', 'read:org'] + +SOCIAL_AUTH_GITHUB_ORG_KEY = '' +SOCIAL_AUTH_GITHUB_ORG_SECRET = '' +SOCIAL_AUTH_GITHUB_ORG_NAME = '' +SOCIAL_AUTH_GITHUB_ORG_SCOPE = ['user:email', 'read:org'] + +SOCIAL_AUTH_GITHUB_TEAM_KEY = '' +SOCIAL_AUTH_GITHUB_TEAM_SECRET = '' +SOCIAL_AUTH_GITHUB_TEAM_ID = '' +SOCIAL_AUTH_GITHUB_TEAM_SCOPE = ['user:email', 'read:org'] + +SOCIAL_AUTH_AZUREAD_OAUTH2_KEY = '' +SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET = '' + +SOCIAL_AUTH_SAML_SP_ENTITY_ID = '' +SOCIAL_AUTH_SAML_SP_PUBLIC_CERT = '' +SOCIAL_AUTH_SAML_SP_PRIVATE_KEY = '' +SOCIAL_AUTH_SAML_ORG_INFO = {} +SOCIAL_AUTH_SAML_TECHNICAL_CONTACT = {} +SOCIAL_AUTH_SAML_SUPPORT_CONTACT = {} +SOCIAL_AUTH_SAML_ENABLED_IDPS = {} + +SOCIAL_AUTH_SAML_ORGANIZATION_ATTR = {} +SOCIAL_AUTH_SAML_TEAM_ATTR = {} + +# Any ANSIBLE_* settings will be passed to the subprocess environment by the +# celery task. + +# Do not want AWX to ask interactive questions and want it to be friendly with +# reprovisioning +ANSIBLE_HOST_KEY_CHECKING = False + +# RHEL has too old of an SSH so ansible will select paramiko and this is VERY +# slow. +ANSIBLE_PARAMIKO_RECORD_HOST_KEYS = False + +# Force ansible in color even if we don't have a TTY so we can properly colorize +# output +ANSIBLE_FORCE_COLOR = True + +# Additional environment variables to be passed to the subprocess started by +# the celery task. +AWX_TASK_ENV = {} + +# Flag to enable/disable updating hosts M2M when saving job events. +CAPTURE_JOB_EVENT_HOSTS = False + +# Rebuild Host Smart Inventory memberships. +AWX_REBUILD_SMART_MEMBERSHIP = False + +# Enable bubblewrap support for running jobs (playbook runs only). +# Note: This setting may be overridden by database settings. +AWX_PROOT_ENABLED = True + +# Command/path to bubblewrap. +AWX_PROOT_CMD = 'bwrap' + +# Additional paths to hide from jobs using bubblewrap. +# Note: This setting may be overridden by database settings. +AWX_PROOT_HIDE_PATHS = [] + +# Additional paths to show for jobs using bubbelwrap. +# Note: This setting may be overridden by database settings. +AWX_PROOT_SHOW_PATHS = [] + +# Number of jobs to show as part of the job template history +AWX_JOB_TEMPLATE_HISTORY = 10 + +# The directory in which Tower will create new temporary directories for job +# execution and isolation (such as credential files and custom +# inventory scripts). +# Note: This setting may be overridden by database settings. +AWX_PROOT_BASE_PATH = "/tmp" + +# User definable ansible callback plugins +# Note: This setting may be overridden by database settings. +AWX_ANSIBLE_CALLBACK_PLUGINS = "" + +# Automatically remove nodes that have missed their heartbeats after some time +AWX_AUTO_DEPROVISION_INSTANCES = False + +# Enable Pendo on the UI, possible values are 'off', 'anonymous', and 'detailed' +# Note: This setting may be overridden by database settings. +PENDO_TRACKING_STATE = "off" + +# Default list of modules allowed for ad hoc commands. +# Note: This setting may be overridden by database settings. +AD_HOC_COMMANDS = [ + 'command', + 'shell', + 'yum', + 'apt', + 'apt_key', + 'apt_repository', + 'apt_rpm', + 'service', + 'group', + 'user', + 'mount', + 'ping', + 'selinux', + 'setup', + 'win_ping', + 'win_service', + 'win_updates', + 'win_group', + 'win_user', +] + +INV_ENV_VARIABLE_BLACKLIST = ("HOME", "USER", "_", "TERM") + +# ---------------- +# -- Amazon EC2 -- +# ---------------- + +# EC2_REGION_NAMES uses translation text, so axed + +EC2_REGIONS_BLACKLIST = [ + 'us-gov-west-1', + 'cn-north-1', +] + +# Inventory variable name/values for determining if host is active/enabled. +EC2_ENABLED_VAR = 'ec2_state' +EC2_ENABLED_VALUE = 'running' + +# Inventory variable name containing unique instance ID. +EC2_INSTANCE_ID_VAR = 'ec2_id' + +# Filter for allowed group/host names when importing inventory from EC2. +EC2_GROUP_FILTER = r'^.+$' +EC2_HOST_FILTER = r'^.+$' +EC2_EXCLUDE_EMPTY_GROUPS = True + + +# ------------ +# -- VMware -- +# ------------ +VMWARE_REGIONS_BLACKLIST = [] + +# Inventory variable name/values for determining whether a host is +# active in vSphere. +VMWARE_ENABLED_VAR = 'guest.gueststate' +VMWARE_ENABLED_VALUE = 'running' + +# Inventory variable name containing the unique instance ID. +VMWARE_INSTANCE_ID_VAR = 'config.instanceuuid' + +# Filter for allowed group and host names when importing inventory +# from VMware. +VMWARE_GROUP_FILTER = r'^.+$' +VMWARE_HOST_FILTER = r'^.+$' +VMWARE_EXCLUDE_EMPTY_GROUPS = True + +VMWARE_VALIDATE_CERTS = False +# --------------------------- +# -- Google Compute Engine -- +# --------------------------- + +# GCE_REGION_CHOICES uses translation text, axed +GCE_REGIONS_BLACKLIST = [] + +# Inventory variable name/value for determining whether a host is active +# in Google Compute Engine. +GCE_ENABLED_VAR = 'status' +GCE_ENABLED_VALUE = 'running' + +# Filter for allowed group and host names when importing inventory from +# Google Compute Engine. +GCE_GROUP_FILTER = r'^.+$' +GCE_HOST_FILTER = r'^.+$' +GCE_EXCLUDE_EMPTY_GROUPS = True +GCE_INSTANCE_ID_VAR = None + +# -------------------------------------- +# -- Microsoft Azure Resource Manager -- +# -------------------------------------- + +# axed AZURE_RM_REGION_CHOICES +AZURE_RM_REGIONS_BLACKLIST = [] + +AZURE_RM_GROUP_FILTER = r'^.+$' +AZURE_RM_HOST_FILTER = r'^.+$' +AZURE_RM_ENABLED_VAR = 'powerstate' +AZURE_RM_ENABLED_VALUE = 'running' +AZURE_RM_INSTANCE_ID_VAR = 'id' +AZURE_RM_EXCLUDE_EMPTY_GROUPS = True + +# --------------------- +# ----- OpenStack ----- +# --------------------- +OPENSTACK_ENABLED_VAR = 'status' +OPENSTACK_ENABLED_VALUE = 'ACTIVE' +OPENSTACK_GROUP_FILTER = r'^.+$' +OPENSTACK_HOST_FILTER = r'^.+$' +OPENSTACK_EXCLUDE_EMPTY_GROUPS = True +OPENSTACK_INSTANCE_ID_VAR = 'openstack.id' + +# --------------------- +# ----- oVirt4 ----- +# --------------------- +RHV_ENABLED_VAR = 'status' +RHV_ENABLED_VALUE = 'up' +RHV_GROUP_FILTER = r'^.+$' +RHV_HOST_FILTER = r'^.+$' +RHV_EXCLUDE_EMPTY_GROUPS = True +RHV_INSTANCE_ID_VAR = 'id' + +# --------------------- +# ----- Tower ----- +# --------------------- +TOWER_ENABLED_VAR = 'remote_tower_enabled' +TOWER_ENABLED_VALUE = 'true' +TOWER_GROUP_FILTER = r'^.+$' +TOWER_HOST_FILTER = r'^.+$' +TOWER_EXCLUDE_EMPTY_GROUPS = True +TOWER_INSTANCE_ID_VAR = 'remote_tower_id' + +# --------------------- +# ----- Foreman ----- +# --------------------- +SATELLITE6_ENABLED_VAR = 'foreman.enabled' +SATELLITE6_ENABLED_VALUE = 'True' +SATELLITE6_GROUP_FILTER = r'^.+$' +SATELLITE6_HOST_FILTER = r'^.+$' +SATELLITE6_EXCLUDE_EMPTY_GROUPS = True +SATELLITE6_INSTANCE_ID_VAR = 'foreman.id' +SATELLITE6_GROUP_PREFIX = 'foreman_' +SATELLITE6_GROUP_PATTERNS = ["{app}-{tier}-{color}", "{app}-{color}", "{app}", "{tier}"] + +# --------------------- +# ----- CloudForms ----- +# --------------------- +CLOUDFORMS_ENABLED_VAR = 'cloudforms.power_state' +CLOUDFORMS_ENABLED_VALUE = 'on' +CLOUDFORMS_GROUP_FILTER = r'^.+$' +CLOUDFORMS_HOST_FILTER = r'^.+$' +CLOUDFORMS_EXCLUDE_EMPTY_GROUPS = True +CLOUDFORMS_INSTANCE_ID_VAR = 'cloudforms.id' + +# --------------------- +# ----- Custom ----- +# --------------------- +#CUSTOM_ENABLED_VAR = +#CUSTOM_ENABLED_VALUE = +CUSTOM_GROUP_FILTER = r'^.+$' +CUSTOM_HOST_FILTER = r'^.+$' +CUSTOM_EXCLUDE_EMPTY_GROUPS = True +#CUSTOM_INSTANCE_ID_VAR = + +# --------------------- +# ----- SCM ----- +# --------------------- +#SCM_ENABLED_VAR = +#SCM_ENABLED_VALUE = +SCM_GROUP_FILTER = r'^.+$' +SCM_HOST_FILTER = r'^.+$' +SCM_EXCLUDE_EMPTY_GROUPS = True +#SCM_INSTANCE_ID_VAR = + +# --------------------- +# -- Activity Stream -- +# --------------------- +# Defaults for enabling/disabling activity stream. +# Note: These settings may be overridden by database settings. +ACTIVITY_STREAM_ENABLED = True +ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC = False + +# Internal API URL for use by inventory scripts and callback plugin. +INTERNAL_API_URL = 'http://127.0.0.1:%s' % DEVSERVER_DEFAULT_PORT + +PERSISTENT_CALLBACK_MESSAGES = True +USE_CALLBACK_QUEUE = True +CALLBACK_QUEUE = "callback_tasks" +FACT_QUEUE = "facts" + +SCHEDULER_QUEUE = "scheduler" + +TASK_COMMAND_PORT = 6559 + +SOCKETIO_NOTIFICATION_PORT = 6557 +SOCKETIO_LISTEN_PORT = 8080 + +FACT_CACHE_PORT = 6564 + +# Note: This setting may be overridden by database settings. +ORG_ADMINS_CAN_SEE_ALL_USERS = True + +# Note: This setting may be overridden by database settings. +TOWER_ADMIN_ALERTS = True + +# Note: This setting may be overridden by database settings. +TOWER_URL_BASE = "https://towerhost" + +INSIGHTS_URL_BASE = "https://access.redhat.com" + +TOWER_SETTINGS_MANIFEST = {} + +# Settings related to external logger configuration +LOG_AGGREGATOR_ENABLED = False +LOG_AGGREGATOR_TCP_TIMEOUT = 5 +LOG_AGGREGATOR_VERIFY_CERT = True +LOG_AGGREGATOR_LEVEL = 'INFO' + +# The number of retry attempts for websocket session establishment +# If you're encountering issues establishing websockets in clustered Tower, +# raising this value can help +CHANNEL_LAYER_RECEIVE_MAX_RETRY = 10 + +# Logging configuration. +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'simple': { + 'format': '%(asctime)s %(levelname)-8s %(name)s %(message)s', + }, + # FIXME: you will need this logger to run inventory imports + # 'timed_import': { + # '()': 'awx.main.utils.formatters.TimeFormatter', + # 'format': '%(relativeSeconds)9.3f %(levelname)-8s %(message)s' + # } + }, + 'handlers': { + 'console': { + '()': 'logging.StreamHandler', + 'level': 'DEBUG', + 'formatter': 'simple', + }, + 'null': { + 'class': 'logging.NullHandler', + }, + # 'inventory_import': { + # 'level': 'DEBUG', + # 'class':'logging.StreamHandler', + # 'formatter': 'timed_import', + # }, + }, + 'loggers': { + 'django': { + 'handlers': ['console'], + }, + 'django.request': { + 'handlers': ['console'], + 'level': 'WARNING', + }, + 'rest_framework.request': { + 'handlers': ['console'], + 'level': 'WARNING', + 'propagate': False, + }, + 'py.warnings': { + 'handlers': ['console'], + }, + 'awx': { + 'handlers': ['console'], + 'level': 'DEBUG', + }, + # 'awx.main.commands.inventory_import': { + # 'handlers': ['inventory_import'], + # 'propagate': False + # }, + 'awx.main.consumers': { + 'handlers': ['null'] + }, + 'awx.main.access': { + 'handlers': ['null'], + 'propagate': False, + }, + 'awx.analytics': { + 'handlers': ['null'], + 'level': 'INFO', + 'propagate': False + }, + 'django_auth_ldap': { + 'handlers': ['console'], + 'level': 'DEBUG', + }, + 'social': { + 'handlers': ['console'], + 'level': 'DEBUG', + }, + } +} +# Apply coloring to messages logged to the console +COLOR_LOGS = False + +# https://github.com/django-polymorphic/django-polymorphic/issues/195 +# FIXME: Disabling models.E006 warning until we can renamed Project and InventorySource +SILENCED_SYSTEM_CHECKS = ['models.E006'] + +# Use middleware to get request statistics +AWX_REQUEST_PROFILE = False + +# ----- start settings from development.py ------ + + + +# awx-manage shell_plus --notebook +NOTEBOOK_ARGUMENTS = [ + '--NotebookApp.token=', + '--ip', '0.0.0.0', + '--port', '8888', + '--allow-root', + '--no-browser', +] + +# print SQL queries in shell_plus +SHELL_PLUS_PRINT_SQL = False + +# show colored logs in the dev environment +# to disable this, set `COLOR_LOGS = False` in awx/settings/local_settings.py +LOGGING['handlers']['console']['()'] = 'awx.main.utils.handlers.ColorHandler' +COLOR_LOGS = True + +ALLOWED_HOSTS = ['*'] + +mimetypes.add_type("image/svg+xml", ".svg", True) +mimetypes.add_type("image/svg+xml", ".svgz", True) + +# Disallow sending session cookies over insecure connections +SESSION_COOKIE_SECURE = False + +# Disallow sending csrf cookies over insecure connections +CSRF_COOKIE_SECURE = False + +# Override django.template.loaders.cached.Loader in defaults.py +template = next((tpl_backend for tpl_backend in TEMPLATES if tpl_backend['NAME'] == 'default'), None) # noqa +template['OPTIONS']['loaders'] = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', +) + +# Disable capturing all SQL queries when running celeryd in development. +if 'celery' in sys.argv: + SQL_DEBUG = False + +CELERYD_HIJACK_ROOT_LOGGER = False +CELERYD_LOG_COLOR = True + +CALLBACK_QUEUE = "callback_tasks" + +# Enable PROOT for tower-qa integration tests. +# Note: This setting may be overridden by database settings. +AWX_PROOT_ENABLED = True + +AWX_ISOLATED_USERNAME = 'root' +AWX_ISOLATED_CHECK_INTERVAL = 1 +AWX_ISOLATED_LAUNCH_TIMEOUT = 30 + +# Disable Pendo on the UI for development/test. +# Note: This setting may be overridden by database settings. +PENDO_TRACKING_STATE = "off" + +# Use Django-Jenkins if installed. Only run tests for awx.main app. +try: + import django_jenkins + INSTALLED_APPS += (django_jenkins.__name__,) + PROJECT_APPS = ('awx.main.tests', 'awx.api.tests',) +except ImportError: + pass + +if 'django_jenkins' in INSTALLED_APPS: + JENKINS_TASKS = ( + # 'django_jenkins.tasks.run_pylint', + # 'django_jenkins.tasks.run_flake8', + # The following are not needed when including run_flake8 + # 'django_jenkins.tasks.run_pep8', + # 'django_jenkins.tasks.run_pyflakes', + # The following are handled by various grunt tasks and no longer required + # 'django_jenkins.tasks.run_jshint', + # 'django_jenkins.tasks.run_csslint', + ) + PEP8_RCFILE = "setup.cfg" + PYLINT_RCFILE = ".pylintrc" + +INSTALLED_APPS += ('rest_framework_swagger',) + +# Much faster than the default +# https://docs.djangoproject.com/en/1.6/topics/auth/passwords/#how-django-stores-passwords +PASSWORD_HASHERS = ( + 'django.contrib.auth.hashers.MD5PasswordHasher', + 'django.contrib.auth.hashers.PBKDF2PasswordHasher', +) + +# Configure a default UUID for development only. +SYSTEM_UUID = '00000000-0000-0000-0000-000000000000' + +# Store a snapshot of default settings at this point before loading any +# customizable config files. +DEFAULTS_SNAPSHOT = {} +this_module = sys.modules[__name__] +for setting in dir(this_module): + if setting == setting.upper(): + DEFAULTS_SNAPSHOT[setting] = copy.deepcopy(getattr(this_module, setting)) + + +BASE_VENV_PATH = os.getenv('VIRTUAL_ENV', '/usr/local') +ANSIBLE_VENV_PATH = BASE_VENV_PATH +AWX_VENV_PATH = BASE_VENV_PATH + + +CLUSTER_HOST_ID = 'tower' + +# Supervisor service name dictionary used for programatic restart +SERVICE_NAME_DICT = { + "celery": "celery", + "callback": "receiver", + "runworker": "channels", + "uwsgi": "uwsgi", + "daphne": "daphne", + "nginx": "nginx"} +# Used for sending commands in automatic restart +# UWSGI_FIFO_LOCATION = '/awxfifo' + diff --git a/tests_awx/conftest.py b/tests_awx/conftest.py new file mode 100644 index 00000000..a4f89547 --- /dev/null +++ b/tests_awx/conftest.py @@ -0,0 +1,99 @@ +# Python +import pytest + +# Django +from django.core.urlresolvers import resolve +from django.utils.six.moves.urllib.parse import urlparse +from django.contrib.auth.models import User + +from rest_framework.test import ( + APIRequestFactory, + force_authenticate, +) + +# AWX +from awx.main.models import User + + +@pytest.fixture +def admin(): + return User.objects.create(username='admin_user', is_superuser=True) + + +def _request(verb): + def rf(url, data_or_user=None, user=None, middleware=None, expect=None, **kwargs): + if type(data_or_user) is User and user is None: + user = data_or_user + elif 'data' not in kwargs: + kwargs['data'] = data_or_user + if 'format' not in kwargs and 'content_type' not in kwargs: + kwargs['format'] = 'json' + + view, view_args, view_kwargs = resolve(urlparse(url)[2]) + request = getattr(APIRequestFactory(), verb)(url, **kwargs) + if isinstance(kwargs.get('cookies', None), dict): + for key, value in kwargs['cookies'].items(): + request.COOKIES[key] = value + if middleware: + middleware.process_request(request) + if user: + force_authenticate(request, user=user) + + response = view(request, *view_args, **view_kwargs) + if middleware: + middleware.process_response(request, response) + if expect: + if response.status_code != expect: + if getattr(response, 'data', None): + try: + data_copy = response.data.copy() + # Make translated strings printable + for key, value in response.data.items(): + if isinstance(value, list): + response.data[key] = [] + for item in value: + response.data[key].append(str(item)) + else: + response.data[key] = str(value) + except Exception: + response.data = data_copy + assert response.status_code == expect + if hasattr(response, 'render'): + response.render() + return response + return rf + + +@pytest.fixture +def post(): + return _request('post') + + +@pytest.fixture +def get(): + return _request('get') + + +@pytest.fixture +def put(): + return _request('put') + + +@pytest.fixture +def patch(): + return _request('patch') + + +@pytest.fixture +def delete(): + return _request('delete') + + +@pytest.fixture +def head(): + return _request('head') + + +@pytest.fixture +def options(): + return _request('options') diff --git a/tests_awx/test_organization.py b/tests_awx/test_organization.py new file mode 100644 index 00000000..d1ee492b --- /dev/null +++ b/tests_awx/test_organization.py @@ -0,0 +1,9 @@ +import pytest + +from awx.api.versioning import reverse + + +@pytest.mark.django_db +def test_create_org(post, admin): + url = reverse('api:organization_list') + r = post(url, data={'name': 'anorg'}, user=admin, expect=201) From b89e4deed8e2cf04c3c35d036174284c5e832552 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 14 Mar 2018 08:41:14 -0400 Subject: [PATCH 2/9] hack hack hack --- .travis.yml | 4 ++-- install_awx.sh | 3 ++- tests_awx/conftest.py | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index d058a55a..6d8dcb91 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,9 +10,9 @@ python: matrix: allow_failures: - python: nightly -before_install: - - ./install_awx.sh install: + - python install_awx_req.py + - ./install_awx.sh - pip install -r requirements_dev.txt script: - flake8 tower-cli/ tests/ diff --git a/install_awx.sh b/install_awx.sh index 7be4593b..835f20aa 100755 --- a/install_awx.sh +++ b/install_awx.sh @@ -1,8 +1,9 @@ rm -rf awx/ git clone https://github.com/ansible/awx.git awx --depth=1 cp tests_awx/CLI_settings.py awx/awx/settings/CLI_settings.py -python install_awx_req.py cd awx +rm awx/sso/__init__.py +touch awx/sso/__init__.py python setup.py install cd .. # have to add awx dir to path diff --git a/tests_awx/conftest.py b/tests_awx/conftest.py index a4f89547..7e63c45e 100644 --- a/tests_awx/conftest.py +++ b/tests_awx/conftest.py @@ -4,7 +4,6 @@ # Django from django.core.urlresolvers import resolve from django.utils.six.moves.urllib.parse import urlparse -from django.contrib.auth.models import User from rest_framework.test import ( APIRequestFactory, From 4979b27e80a70edb517e9adb142000dba31b72f3 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 14 Mar 2018 08:58:45 -0400 Subject: [PATCH 3/9] wrong order --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6d8dcb91..7ad33ebd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,8 @@ matrix: allow_failures: - python: nightly install: - - python install_awx_req.py - ./install_awx.sh + - python install_awx_req.py - pip install -r requirements_dev.txt script: - flake8 tower-cli/ tests/ From 18b4b05fee71a0a529c471176a141372032dc4b4 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 14 Mar 2018 09:19:19 -0400 Subject: [PATCH 4/9] add git req --- install_awx_req.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install_awx_req.py b/install_awx_req.py index eafa243a..76bbfe36 100644 --- a/install_awx_req.py +++ b/install_awx_req.py @@ -1,6 +1,6 @@ from subprocess import call -files = ['requirements.txt', 'requirements_dev.txt'] +files = ['requirements.txt', 'requirements_dev.txt', 'requirements_git.txt'] seen = set([]) From 3b3d81c8cff78a84da5e7ed8bbd645561bb32913 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 14 Mar 2018 09:32:16 -0400 Subject: [PATCH 5/9] install from in req --- install_awx_req.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install_awx_req.py b/install_awx_req.py index 76bbfe36..6b64e5e2 100644 --- a/install_awx_req.py +++ b/install_awx_req.py @@ -1,6 +1,6 @@ from subprocess import call -files = ['requirements.txt', 'requirements_dev.txt', 'requirements_git.txt'] +files = ['requirements.in', 'requirements_dev.txt', 'requirements_git.txt'] seen = set([]) @@ -30,5 +30,5 @@ failed.add(target) if failed: - print 'tower-cli AWX integration failed to install packages \n' - print ' - \n'.join(failed) + print('tower-cli AWX integration failed to install packages \n') + print(' - \n'.join(failed)) From 6089c9a4948d33d7eb8fe9630a07234b6bb543a6 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 14 Mar 2018 15:56:10 -0400 Subject: [PATCH 6/9] fix things --- .gitignore | 1 + install_awx.sh | 1 - pytest.ini | 2 +- tests_awx/CLI_settings.py | 1104 -------------------------------- tests_awx/test_organization.py | 19 + tower_cli/api.py | 70 +- 6 files changed, 87 insertions(+), 1110 deletions(-) delete mode 100644 tests_awx/CLI_settings.py diff --git a/.gitignore b/.gitignore index f2798d80..cc270992 100644 --- a/.gitignore +++ b/.gitignore @@ -82,3 +82,4 @@ setup_v2.py # for AWX install /awx .cache/ +awx.sqlite3 diff --git a/install_awx.sh b/install_awx.sh index 835f20aa..92158120 100755 --- a/install_awx.sh +++ b/install_awx.sh @@ -1,6 +1,5 @@ rm -rf awx/ git clone https://github.com/ansible/awx.git awx --depth=1 -cp tests_awx/CLI_settings.py awx/awx/settings/CLI_settings.py cd awx rm awx/sso/__init__.py touch awx/sso/__init__.py diff --git a/pytest.ini b/pytest.ini index 0e332705..0003f059 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,4 +1,4 @@ [pytest] -DJANGO_SETTINGS_MODULE = awx.settings.CLI_settings +DJANGO_SETTINGS_MODULE = awx.settings.development python_files = *.py addopts = --reuse-db --nomigrations --tb=native \ No newline at end of file diff --git a/tests_awx/CLI_settings.py b/tests_awx/CLI_settings.py deleted file mode 100644 index 6fafcbca..00000000 --- a/tests_awx/CLI_settings.py +++ /dev/null @@ -1,1104 +0,0 @@ - - -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -import os -import re # noqa -import sys -import ldap -import djcelery -from datetime import timedelta - -from kombu import Queue, Exchange -from kombu.common import Broadcast - -# global settings -from django.conf import global_settings -# ugettext lazy -# from django.utils.translation import ugettext_lazy as _ - -# Python -import copy - -# Centos-7 doesn't include the svg mime type -# /usr/lib64/python/mimetypes.py -import mimetypes - - -# Update this module's local settings from the global settings module. -this_module = sys.modules[__name__] -for setting in dir(global_settings): - if setting == setting.upper(): - setattr(this_module, setting, getattr(global_settings, setting)) - -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(__file__)) - - -def is_testing(argv=None): - import sys - '''Return True if running django or py.test unit tests.''' - argv = sys.argv if argv is None else argv - if len(argv) >= 1 and ('py.test' in argv[0] or 'py/test.py' in argv[0]): - return True - elif len(argv) >= 2 and argv[1] == 'test': - return True - return False - - -def IS_TESTING(argv=None): - return is_testing(argv) - - -DEBUG = True -SQL_DEBUG = DEBUG - -ADMINS = ( - # ('Your Name', 'your_email@domain.com'), -) - -MANAGERS = ADMINS - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'awx.sqlite3'), - 'ATOMIC_REQUESTS': True, - 'TEST': { - # Test database cannot be :memory: for celery/inventory tests. - 'NAME': os.path.join(BASE_DIR, 'awx_test.sqlite3'), - }, - } -} - -# Internationalization -# https://docs.djangoproject.com/en/dev/topics/i18n/ -# -# Local time zone for this installation. Choices can be found here: -# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name -# although not all choices may be available on all operating systems. -# On Unix systems, a value of None will cause Django to use the same -# timezone as the operating system. -# If running in a Windows environment this must be set to the same as your -# system time zone. -TIME_ZONE = 'UTC' - -# Language code for this installation. All choices can be found here: -# http://www.i18nguy.com/unicode/language-identifiers.html -LANGUAGE_CODE = 'en-us' - -# If you set this to False, Django will make some optimizations so as not -# to load the internationalization machinery. -USE_I18N = True - -# If you set this to False, Django will not format dates, numbers and -# calendars according to the current locale -USE_L10N = True - -USE_TZ = True - -STATICFILES_DIRS = ( - os.path.join(BASE_DIR, 'ui', 'static'), - os.path.join(BASE_DIR, 'static'), -) - -# Absolute filesystem path to the directory where static file are collected via -# the collectstatic command. -STATIC_ROOT = os.path.join(BASE_DIR, 'public', 'static') - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/dev/howto/static-files/ -STATIC_URL = '/static/' - -# Absolute filesystem path to the directory that will hold user-uploaded files. -# Example: "/home/media/media.lawrence.com/" -MEDIA_ROOT = os.path.join(BASE_DIR, 'public', 'media') - -# URL that handles the media served from MEDIA_ROOT. Make sure to use a -# trailing slash if there is a path component (optional in other cases). -# Examples: "http://media.lawrence.com", "http://example.com/media/" -MEDIA_URL = '/media/' - -LOGIN_URL = '/api/login/' - -# Absolute filesystem path to the directory to host projects (with playbooks). -# This directory should not be web-accessible. -PROJECTS_ROOT = os.path.join(BASE_DIR, 'projects') - -# Absolute filesystem path to the directory for job status stdout (default for -# development and tests, default for production defined in production.py). This -# directory should not be web-accessible -JOBOUTPUT_ROOT = os.path.join(BASE_DIR, 'job_output') - -# Absolute filesystem path to the directory to store logs -LOG_ROOT = os.path.join(BASE_DIR) - -# The heartbeat file for the tower scheduler -SCHEDULE_METADATA_LOCATION = os.path.join(BASE_DIR, '.tower_cycle') - -# Django gettext files path: locale//LC_MESSAGES/django.po, django.mo -LOCALE_PATHS = ( - os.path.join(BASE_DIR, 'locale'), -) - -# Graph of resources that can have named-url -NAMED_URL_GRAPH = {} - -# Maximum number of the same job that can be waiting to run when launching from scheduler -# Note: This setting may be overridden by database settings. -SCHEDULE_MAX_JOBS = 10 - -SITE_ID = 1 - -# Make this unique, and don't share it with anybody. -SECRET_KEY = 'p7z7g1ql4%6+(6nlebb6hdk7sd^&fnjpal308%n%+p^_e6vo1y' - -# Hosts/domain names that are valid for this site; required if DEBUG is False -# See https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts -ALLOWED_HOSTS = [] - -# HTTP headers and meta keys to search to determine remote host name or IP. Add -# additional items to this list, such as "HTTP_X_FORWARDED_FOR", if behind a -# reverse proxy. -REMOTE_HOST_HEADERS = ['REMOTE_ADDR', 'REMOTE_HOST'] - -# If Tower is behind a reverse proxy/load balancer, use this setting to -# whitelist the proxy IP addresses from which Tower should trust custom -# REMOTE_HOST_HEADERS header values -# REMOTE_HOST_HEADERS = ['HTTP_X_FORWARDED_FOR', ''REMOTE_ADDR', 'REMOTE_HOST'] -# PROXY_IP_WHITELIST = ['10.0.1.100', '10.0.1.101'] -# If this setting is an empty list (the default), the headers specified by -# REMOTE_HOST_HEADERS will be trusted unconditionally') -PROXY_IP_WHITELIST = [] - -# Note: This setting may be overridden by database settings. -STDOUT_MAX_BYTES_DISPLAY = 1048576 - -# Returned in the header on event api lists as a recommendation to the UI -# on how many events to display before truncating/hiding -MAX_UI_JOB_EVENTS = 4000 - -# The maximum size of the ansible callback event's res data structure -# beyond this limit and the value will be removed -MAX_EVENT_RES_DATA = 700000 - -# Note: This setting may be overridden by database settings. -EVENT_STDOUT_MAX_BYTES_DISPLAY = 1024 - -# The amount of time before a stdout file is expired and removed locally -# Note that this can be recreated if the stdout is downloaded -LOCAL_STDOUT_EXPIRE_TIME = 2592000 - -# The number of processes spawned by the callback receiver to process job -# events into the database -JOB_EVENT_WORKERS = 4 - -# The maximum size of the job event worker queue before requests are blocked -JOB_EVENT_MAX_QUEUE_SIZE = 10000 - -# Disallow sending session cookies over insecure connections -SESSION_COOKIE_SECURE = True - -# Seconds before sessions expire. -# Note: This setting may be overridden by database settings. -SESSION_COOKIE_AGE = 1209600 - -# Maximum number of per-user valid, concurrent sessions. -# -1 is unlimited -# Note: This setting may be overridden by database settings. -SESSIONS_PER_USER = -1 - -# Disallow sending csrf cookies over insecure connections -CSRF_COOKIE_SECURE = True - -# Limit CSRF cookies to browser sessions -CSRF_COOKIE_AGE = None - -TEMPLATES = [ - { - 'NAME': 'default', - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'OPTIONS': { - 'debug': DEBUG, - 'context_processors': [# NOQA - 'django.contrib.auth.context_processors.auth', - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.template.context_processors.i18n', - 'django.template.context_processors.media', - 'django.template.context_processors.static', - 'django.template.context_processors.tz', - 'django.contrib.messages.context_processors.messages', - 'awx.ui.context_processors.settings', - 'awx.ui.context_processors.version', - 'social_django.context_processors.backends', - 'social_django.context_processors.login_redirect', - ], - 'loaders': [( - 'django.template.loaders.cached.Loader', - ('django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader',), - )], - 'builtins': ['awx.main.templatetags.swagger'], - }, - 'DIRS': [ - os.path.join(BASE_DIR, 'templates'), - ], - }, -] - -MIDDLEWARE_CLASSES = ( # NOQA - 'awx.main.middleware.MigrationRanCheckMiddleware', - 'awx.main.middleware.TimingMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.locale.LocaleMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'awx.main.middleware.ActivityStreamMiddleware', - 'awx.sso.middleware.SocialAuthMiddleware', - 'crum.CurrentRequestUserMiddleware', - 'awx.main.middleware.URLModificationMiddleware', -) - - -ROOT_URLCONF = 'awx.urls' - -WSGI_APPLICATION = 'awx.wsgi.application' - -INSTALLED_APPS = ( - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.messages', - 'django.contrib.sessions', - 'django.contrib.sites', - 'django.contrib.staticfiles', - 'oauth2_provider', - 'rest_framework', - 'django_extensions', - 'djcelery', - 'channels', - 'polymorphic', - 'taggit', - 'social_django', - 'awx.conf', - 'awx.main', - 'awx.api', - 'awx.ui', - 'awx.sso', - 'solo', -) - -INTERNAL_IPS = ('127.0.0.1',) - -MAX_PAGE_SIZE = 200 -REST_FRAMEWORK = { - 'DEFAULT_PAGINATION_CLASS': 'awx.api.pagination.Pagination', - 'PAGE_SIZE': 25, - 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'awx.api.authentication.LoggedOAuth2Authentication', - 'awx.api.authentication.SessionAuthentication', - 'awx.api.authentication.LoggedBasicAuthentication', - ), - 'DEFAULT_PERMISSION_CLASSES': ( - 'awx.api.permissions.ModelAccessPermission', - ), - 'DEFAULT_FILTER_BACKENDS': ( - 'awx.api.filters.TypeFilterBackend', - 'awx.api.filters.FieldLookupBackend', - 'rest_framework.filters.SearchFilter', - 'awx.api.filters.OrderByBackend', - ), - 'DEFAULT_PARSER_CLASSES': ( - 'awx.api.parsers.JSONParser', - ), - 'DEFAULT_RENDERER_CLASSES': ( - 'rest_framework.renderers.JSONRenderer', - 'awx.api.renderers.BrowsableAPIRenderer', - ), - 'DEFAULT_METADATA_CLASS': 'awx.api.metadata.Metadata', - 'EXCEPTION_HANDLER': 'awx.api.views.api_exception_handler', - 'VIEW_NAME_FUNCTION': 'awx.api.generics.get_view_name', - 'VIEW_DESCRIPTION_FUNCTION': 'awx.api.generics.get_view_description', - 'NON_FIELD_ERRORS_KEY': '__all__', - 'DEFAULT_VERSION': 'v2', - #'URL_FORMAT_OVERRIDE': None, -} - -AUTHENTICATION_BACKENDS = ( - 'awx.sso.backends.LDAPBackend', - 'awx.sso.backends.LDAPBackend1', - 'awx.sso.backends.LDAPBackend2', - 'awx.sso.backends.LDAPBackend3', - 'awx.sso.backends.LDAPBackend4', - 'awx.sso.backends.LDAPBackend5', - 'awx.sso.backends.RADIUSBackend', - 'awx.sso.backends.TACACSPlusBackend', - 'social_core.backends.google.GoogleOAuth2', - 'social_core.backends.github.GithubOAuth2', - 'social_core.backends.github.GithubOrganizationOAuth2', - 'social_core.backends.github.GithubTeamOAuth2', - 'social_core.backends.azuread.AzureADOAuth2', - 'awx.sso.backends.SAMLAuth', - 'django.contrib.auth.backends.ModelBackend', -) - - -# Django OAuth Toolkit settings -OAUTH2_PROVIDER_APPLICATION_MODEL = 'main.OAuth2Application' -OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = 'main.OAuth2AccessToken' - -OAUTH2_PROVIDER = {'ACCESS_TOKEN_EXPIRE_SECONDS': 31536000000, - 'AUTHORIZATION_CODE_EXPIRE_SECONDS': 600} - -# LDAP server (default to None to skip using LDAP authentication). -# Note: This setting may be overridden by database settings. -AUTH_LDAP_SERVER_URI = None - -# Disable LDAP referrals by default (to prevent certain LDAP queries from -# hanging with AD). -# Note: This setting may be overridden by database settings. -AUTH_LDAP_CONNECTION_OPTIONS = { - ldap.OPT_REFERRALS: 0, - ldap.OPT_NETWORK_TIMEOUT: 30 -} - -# Radius server settings (default to empty string to skip using Radius auth). -# Note: These settings may be overridden by database settings. -RADIUS_SERVER = '' -RADIUS_PORT = 1812 -RADIUS_SECRET = '' - -# TACACS+ settings (default host to empty string to skip using TACACS+ auth). -# Note: These settings may be overridden by database settings. -TACACSPLUS_HOST = '' -TACACSPLUS_PORT = 49 -TACACSPLUS_SECRET = '' -TACACSPLUS_SESSION_TIMEOUT = 5 -TACACSPLUS_AUTH_PROTOCOL = 'ascii' - -# Enable / Disable HTTP Basic Authentication used in the API browser -# Note: Session limits are not enforced when using HTTP Basic Authentication. -# Note: This setting may be overridden by database settings. -AUTH_BASIC_ENABLED = True - -# If set, serve only minified JS for UI. -USE_MINIFIED_JS = False - -# Email address that error messages come from. -SERVER_EMAIL = 'root@localhost' - -# Default email address to use for various automated correspondence from -# the site managers. -DEFAULT_FROM_EMAIL = 'tower@localhost' - -# Subject-line prefix for email messages send with django.core.mail.mail_admins -# or ...mail_managers. Make sure to include the trailing space. -EMAIL_SUBJECT_PREFIX = '[Tower] ' - -# The email backend to use. For possible shortcuts see django.core.mail. -# The default is to use the SMTP backend. -# Third-party backends can be specified by providing a Python path -# to a module that defines an EmailBackend class. -EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' - -# Host for sending email. -EMAIL_HOST = 'localhost' - -# Port for sending email. -EMAIL_PORT = 25 - -# Optional SMTP authentication information for EMAIL_HOST. -EMAIL_HOST_USER = '' -EMAIL_HOST_PASSWORD = '' -EMAIL_USE_TLS = False - -# The number of seconds to sleep between status checks for jobs running on isolated nodes -AWX_ISOLATED_CHECK_INTERVAL = 30 - -# The timeout (in seconds) for launching jobs on isolated nodes -AWX_ISOLATED_LAUNCH_TIMEOUT = 600 - -# Ansible connection timeout (in seconds) for communicating with isolated instances -AWX_ISOLATED_CONNECTION_TIMEOUT = 10 - -# The time (in seconds) between the periodic isolated heartbeat status check -AWX_ISOLATED_PERIODIC_CHECK = 600 - -# Memcached django cache configuration -# CACHES = { -# 'default': { -# 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', -# 'LOCATION': '127.0.0.1:11211', -# 'TIMEOUT': 864000, -# 'KEY_PREFIX': 'tower_dev', -# } -# } - -# Use Django-Debug-Toolbar if installed. -try: - import debug_toolbar - INSTALLED_APPS += (debug_toolbar.__name__,) -except ImportError: - pass - -DEBUG_TOOLBAR_CONFIG = { - 'INTERCEPT_REDIRECTS': False, - 'ENABLE_STACKTRACES' : True, -} - -DEVSERVER_DEFAULT_ADDR = '0.0.0.0' -DEVSERVER_DEFAULT_PORT = '8013' - -# Set default ports for live server tests. -os.environ.setdefault('DJANGO_LIVE_TEST_SERVER_ADDRESS', 'localhost:9013-9199') - -djcelery.setup_loader() - -BROKER_POOL_LIMIT = None -BROKER_URL = 'amqp://guest:guest@localhost:5672//' -CELERY_EVENT_QUEUE_TTL = 5 -CELERY_DEFAULT_QUEUE = 'tower' -CELERY_TASK_SERIALIZER = 'json' -CELERY_RESULT_SERIALIZER = 'json' -CELERY_ACCEPT_CONTENT = ['json'] -CELERY_TRACK_STARTED = True -CELERYD_TASK_TIME_LIMIT = None -CELERYD_TASK_SOFT_TIME_LIMIT = None -CELERYD_POOL_RESTARTS = True -CELERY_RESULT_BACKEND = 'djcelery.backends.database:DatabaseBackend' -CELERY_IMPORTS = ('awx.main.scheduler.tasks',) -CELERY_QUEUES = ( - Queue('tower', Exchange('tower'), routing_key='tower'), - Broadcast('tower_broadcast_all') -) -CELERY_ROUTES = {} - -CELERYBEAT_SCHEDULER = 'celery.beat.PersistentScheduler' -CELERYBEAT_MAX_LOOP_INTERVAL = 60 -CELERYBEAT_SCHEDULE = { - 'tower_scheduler': { - 'task': 'awx.main.tasks.awx_periodic_scheduler', - 'schedule': timedelta(seconds=30), - 'options': {'expires': 20,} - }, - 'admin_checks': { - 'task': 'awx.main.tasks.run_administrative_checks', - 'schedule': timedelta(days=30) - }, - 'cluster_heartbeat': { - 'task': 'awx.main.tasks.cluster_node_heartbeat', - 'schedule': timedelta(seconds=60), - 'options': {'expires': 50,} - }, - 'purge_stdout_files': { - 'task': 'awx.main.tasks.purge_old_stdout_files', - 'schedule': timedelta(days=7) - }, - 'task_manager': { - 'task': 'awx.main.scheduler.tasks.run_task_manager', - 'schedule': timedelta(seconds=20), - 'options': {'expires': 20} - }, - 'isolated_heartbeat': { - 'task': 'awx.main.tasks.awx_isolated_heartbeat', - 'schedule': timedelta(seconds=AWX_ISOLATED_PERIODIC_CHECK), - 'options': {'expires': AWX_ISOLATED_PERIODIC_CHECK * 2}, - } -} -AWX_INCONSISTENT_TASK_INTERVAL = 60 * 3 - -# Celery queues that will always be listened to by celery workers -# Note: Broadcast queues have unique, auto-generated names, with the alias -# property value of the original queue name. -AWX_CELERY_QUEUES_STATIC = ['tower_broadcast_all',] - -ASGI_AMQP = { - 'INIT_FUNC': 'awx.prepare_env', - 'MODEL': 'awx.main.models.channels.ChannelGroup', -} - -# Django Caching Configuration -if is_testing(): - CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', - }, - } -else: - CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', - 'LOCATION': 'memcached:11211', - }, - } - -# Social Auth configuration. -SOCIAL_AUTH_STRATEGY = 'social_django.strategy.DjangoStrategy' -SOCIAL_AUTH_STORAGE = 'social_django.models.DjangoStorage' -SOCIAL_AUTH_USER_MODEL = AUTH_USER_MODEL # noqa - -_SOCIAL_AUTH_PIPELINE_BASE = ( - 'social_core.pipeline.social_auth.social_details', - 'social_core.pipeline.social_auth.social_uid', - 'social_core.pipeline.social_auth.auth_allowed', - 'social_core.pipeline.social_auth.social_user', - 'social_core.pipeline.user.get_username', - 'social_core.pipeline.social_auth.associate_by_email', - 'social_core.pipeline.user.create_user', - 'awx.sso.pipeline.check_user_found_or_created', - 'social_core.pipeline.social_auth.associate_user', - 'social_core.pipeline.social_auth.load_extra_data', - 'awx.sso.pipeline.set_is_active_for_new_user', - 'social_core.pipeline.user.user_details', - 'awx.sso.pipeline.prevent_inactive_login', -) -SOCIAL_AUTH_PIPELINE = _SOCIAL_AUTH_PIPELINE_BASE + ( - 'awx.sso.pipeline.update_user_orgs', - 'awx.sso.pipeline.update_user_teams', -) -SOCIAL_AUTH_SAML_PIPELINE = _SOCIAL_AUTH_PIPELINE_BASE + ( - 'awx.sso.pipeline.update_user_orgs_by_saml_attr', - 'awx.sso.pipeline.update_user_teams_by_saml_attr', - 'awx.sso.pipeline.update_user_orgs', - 'awx.sso.pipeline.update_user_teams', -) - -SOCIAL_AUTH_LOGIN_URL = '/' -SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/sso/complete/' -SOCIAL_AUTH_LOGIN_ERROR_URL = '/sso/error/' -SOCIAL_AUTH_INACTIVE_USER_URL = '/sso/inactive/' - -SOCIAL_AUTH_RAISE_EXCEPTIONS = False -SOCIAL_AUTH_USERNAME_IS_FULL_EMAIL = False -#SOCIAL_AUTH_SLUGIFY_USERNAMES = True -SOCIAL_AUTH_CLEAN_USERNAMES = True - -SOCIAL_AUTH_SANITIZE_REDIRECTS = True -SOCIAL_AUTH_REDIRECT_IS_HTTPS = False - -# Note: These settings may be overridden by database settings. -SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = '' -SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = '' -SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = ['profile'] - -SOCIAL_AUTH_GITHUB_KEY = '' -SOCIAL_AUTH_GITHUB_SECRET = '' -SOCIAL_AUTH_GITHUB_SCOPE = ['user:email', 'read:org'] - -SOCIAL_AUTH_GITHUB_ORG_KEY = '' -SOCIAL_AUTH_GITHUB_ORG_SECRET = '' -SOCIAL_AUTH_GITHUB_ORG_NAME = '' -SOCIAL_AUTH_GITHUB_ORG_SCOPE = ['user:email', 'read:org'] - -SOCIAL_AUTH_GITHUB_TEAM_KEY = '' -SOCIAL_AUTH_GITHUB_TEAM_SECRET = '' -SOCIAL_AUTH_GITHUB_TEAM_ID = '' -SOCIAL_AUTH_GITHUB_TEAM_SCOPE = ['user:email', 'read:org'] - -SOCIAL_AUTH_AZUREAD_OAUTH2_KEY = '' -SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET = '' - -SOCIAL_AUTH_SAML_SP_ENTITY_ID = '' -SOCIAL_AUTH_SAML_SP_PUBLIC_CERT = '' -SOCIAL_AUTH_SAML_SP_PRIVATE_KEY = '' -SOCIAL_AUTH_SAML_ORG_INFO = {} -SOCIAL_AUTH_SAML_TECHNICAL_CONTACT = {} -SOCIAL_AUTH_SAML_SUPPORT_CONTACT = {} -SOCIAL_AUTH_SAML_ENABLED_IDPS = {} - -SOCIAL_AUTH_SAML_ORGANIZATION_ATTR = {} -SOCIAL_AUTH_SAML_TEAM_ATTR = {} - -# Any ANSIBLE_* settings will be passed to the subprocess environment by the -# celery task. - -# Do not want AWX to ask interactive questions and want it to be friendly with -# reprovisioning -ANSIBLE_HOST_KEY_CHECKING = False - -# RHEL has too old of an SSH so ansible will select paramiko and this is VERY -# slow. -ANSIBLE_PARAMIKO_RECORD_HOST_KEYS = False - -# Force ansible in color even if we don't have a TTY so we can properly colorize -# output -ANSIBLE_FORCE_COLOR = True - -# Additional environment variables to be passed to the subprocess started by -# the celery task. -AWX_TASK_ENV = {} - -# Flag to enable/disable updating hosts M2M when saving job events. -CAPTURE_JOB_EVENT_HOSTS = False - -# Rebuild Host Smart Inventory memberships. -AWX_REBUILD_SMART_MEMBERSHIP = False - -# Enable bubblewrap support for running jobs (playbook runs only). -# Note: This setting may be overridden by database settings. -AWX_PROOT_ENABLED = True - -# Command/path to bubblewrap. -AWX_PROOT_CMD = 'bwrap' - -# Additional paths to hide from jobs using bubblewrap. -# Note: This setting may be overridden by database settings. -AWX_PROOT_HIDE_PATHS = [] - -# Additional paths to show for jobs using bubbelwrap. -# Note: This setting may be overridden by database settings. -AWX_PROOT_SHOW_PATHS = [] - -# Number of jobs to show as part of the job template history -AWX_JOB_TEMPLATE_HISTORY = 10 - -# The directory in which Tower will create new temporary directories for job -# execution and isolation (such as credential files and custom -# inventory scripts). -# Note: This setting may be overridden by database settings. -AWX_PROOT_BASE_PATH = "/tmp" - -# User definable ansible callback plugins -# Note: This setting may be overridden by database settings. -AWX_ANSIBLE_CALLBACK_PLUGINS = "" - -# Automatically remove nodes that have missed their heartbeats after some time -AWX_AUTO_DEPROVISION_INSTANCES = False - -# Enable Pendo on the UI, possible values are 'off', 'anonymous', and 'detailed' -# Note: This setting may be overridden by database settings. -PENDO_TRACKING_STATE = "off" - -# Default list of modules allowed for ad hoc commands. -# Note: This setting may be overridden by database settings. -AD_HOC_COMMANDS = [ - 'command', - 'shell', - 'yum', - 'apt', - 'apt_key', - 'apt_repository', - 'apt_rpm', - 'service', - 'group', - 'user', - 'mount', - 'ping', - 'selinux', - 'setup', - 'win_ping', - 'win_service', - 'win_updates', - 'win_group', - 'win_user', -] - -INV_ENV_VARIABLE_BLACKLIST = ("HOME", "USER", "_", "TERM") - -# ---------------- -# -- Amazon EC2 -- -# ---------------- - -# EC2_REGION_NAMES uses translation text, so axed - -EC2_REGIONS_BLACKLIST = [ - 'us-gov-west-1', - 'cn-north-1', -] - -# Inventory variable name/values for determining if host is active/enabled. -EC2_ENABLED_VAR = 'ec2_state' -EC2_ENABLED_VALUE = 'running' - -# Inventory variable name containing unique instance ID. -EC2_INSTANCE_ID_VAR = 'ec2_id' - -# Filter for allowed group/host names when importing inventory from EC2. -EC2_GROUP_FILTER = r'^.+$' -EC2_HOST_FILTER = r'^.+$' -EC2_EXCLUDE_EMPTY_GROUPS = True - - -# ------------ -# -- VMware -- -# ------------ -VMWARE_REGIONS_BLACKLIST = [] - -# Inventory variable name/values for determining whether a host is -# active in vSphere. -VMWARE_ENABLED_VAR = 'guest.gueststate' -VMWARE_ENABLED_VALUE = 'running' - -# Inventory variable name containing the unique instance ID. -VMWARE_INSTANCE_ID_VAR = 'config.instanceuuid' - -# Filter for allowed group and host names when importing inventory -# from VMware. -VMWARE_GROUP_FILTER = r'^.+$' -VMWARE_HOST_FILTER = r'^.+$' -VMWARE_EXCLUDE_EMPTY_GROUPS = True - -VMWARE_VALIDATE_CERTS = False -# --------------------------- -# -- Google Compute Engine -- -# --------------------------- - -# GCE_REGION_CHOICES uses translation text, axed -GCE_REGIONS_BLACKLIST = [] - -# Inventory variable name/value for determining whether a host is active -# in Google Compute Engine. -GCE_ENABLED_VAR = 'status' -GCE_ENABLED_VALUE = 'running' - -# Filter for allowed group and host names when importing inventory from -# Google Compute Engine. -GCE_GROUP_FILTER = r'^.+$' -GCE_HOST_FILTER = r'^.+$' -GCE_EXCLUDE_EMPTY_GROUPS = True -GCE_INSTANCE_ID_VAR = None - -# -------------------------------------- -# -- Microsoft Azure Resource Manager -- -# -------------------------------------- - -# axed AZURE_RM_REGION_CHOICES -AZURE_RM_REGIONS_BLACKLIST = [] - -AZURE_RM_GROUP_FILTER = r'^.+$' -AZURE_RM_HOST_FILTER = r'^.+$' -AZURE_RM_ENABLED_VAR = 'powerstate' -AZURE_RM_ENABLED_VALUE = 'running' -AZURE_RM_INSTANCE_ID_VAR = 'id' -AZURE_RM_EXCLUDE_EMPTY_GROUPS = True - -# --------------------- -# ----- OpenStack ----- -# --------------------- -OPENSTACK_ENABLED_VAR = 'status' -OPENSTACK_ENABLED_VALUE = 'ACTIVE' -OPENSTACK_GROUP_FILTER = r'^.+$' -OPENSTACK_HOST_FILTER = r'^.+$' -OPENSTACK_EXCLUDE_EMPTY_GROUPS = True -OPENSTACK_INSTANCE_ID_VAR = 'openstack.id' - -# --------------------- -# ----- oVirt4 ----- -# --------------------- -RHV_ENABLED_VAR = 'status' -RHV_ENABLED_VALUE = 'up' -RHV_GROUP_FILTER = r'^.+$' -RHV_HOST_FILTER = r'^.+$' -RHV_EXCLUDE_EMPTY_GROUPS = True -RHV_INSTANCE_ID_VAR = 'id' - -# --------------------- -# ----- Tower ----- -# --------------------- -TOWER_ENABLED_VAR = 'remote_tower_enabled' -TOWER_ENABLED_VALUE = 'true' -TOWER_GROUP_FILTER = r'^.+$' -TOWER_HOST_FILTER = r'^.+$' -TOWER_EXCLUDE_EMPTY_GROUPS = True -TOWER_INSTANCE_ID_VAR = 'remote_tower_id' - -# --------------------- -# ----- Foreman ----- -# --------------------- -SATELLITE6_ENABLED_VAR = 'foreman.enabled' -SATELLITE6_ENABLED_VALUE = 'True' -SATELLITE6_GROUP_FILTER = r'^.+$' -SATELLITE6_HOST_FILTER = r'^.+$' -SATELLITE6_EXCLUDE_EMPTY_GROUPS = True -SATELLITE6_INSTANCE_ID_VAR = 'foreman.id' -SATELLITE6_GROUP_PREFIX = 'foreman_' -SATELLITE6_GROUP_PATTERNS = ["{app}-{tier}-{color}", "{app}-{color}", "{app}", "{tier}"] - -# --------------------- -# ----- CloudForms ----- -# --------------------- -CLOUDFORMS_ENABLED_VAR = 'cloudforms.power_state' -CLOUDFORMS_ENABLED_VALUE = 'on' -CLOUDFORMS_GROUP_FILTER = r'^.+$' -CLOUDFORMS_HOST_FILTER = r'^.+$' -CLOUDFORMS_EXCLUDE_EMPTY_GROUPS = True -CLOUDFORMS_INSTANCE_ID_VAR = 'cloudforms.id' - -# --------------------- -# ----- Custom ----- -# --------------------- -#CUSTOM_ENABLED_VAR = -#CUSTOM_ENABLED_VALUE = -CUSTOM_GROUP_FILTER = r'^.+$' -CUSTOM_HOST_FILTER = r'^.+$' -CUSTOM_EXCLUDE_EMPTY_GROUPS = True -#CUSTOM_INSTANCE_ID_VAR = - -# --------------------- -# ----- SCM ----- -# --------------------- -#SCM_ENABLED_VAR = -#SCM_ENABLED_VALUE = -SCM_GROUP_FILTER = r'^.+$' -SCM_HOST_FILTER = r'^.+$' -SCM_EXCLUDE_EMPTY_GROUPS = True -#SCM_INSTANCE_ID_VAR = - -# --------------------- -# -- Activity Stream -- -# --------------------- -# Defaults for enabling/disabling activity stream. -# Note: These settings may be overridden by database settings. -ACTIVITY_STREAM_ENABLED = True -ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC = False - -# Internal API URL for use by inventory scripts and callback plugin. -INTERNAL_API_URL = 'http://127.0.0.1:%s' % DEVSERVER_DEFAULT_PORT - -PERSISTENT_CALLBACK_MESSAGES = True -USE_CALLBACK_QUEUE = True -CALLBACK_QUEUE = "callback_tasks" -FACT_QUEUE = "facts" - -SCHEDULER_QUEUE = "scheduler" - -TASK_COMMAND_PORT = 6559 - -SOCKETIO_NOTIFICATION_PORT = 6557 -SOCKETIO_LISTEN_PORT = 8080 - -FACT_CACHE_PORT = 6564 - -# Note: This setting may be overridden by database settings. -ORG_ADMINS_CAN_SEE_ALL_USERS = True - -# Note: This setting may be overridden by database settings. -TOWER_ADMIN_ALERTS = True - -# Note: This setting may be overridden by database settings. -TOWER_URL_BASE = "https://towerhost" - -INSIGHTS_URL_BASE = "https://access.redhat.com" - -TOWER_SETTINGS_MANIFEST = {} - -# Settings related to external logger configuration -LOG_AGGREGATOR_ENABLED = False -LOG_AGGREGATOR_TCP_TIMEOUT = 5 -LOG_AGGREGATOR_VERIFY_CERT = True -LOG_AGGREGATOR_LEVEL = 'INFO' - -# The number of retry attempts for websocket session establishment -# If you're encountering issues establishing websockets in clustered Tower, -# raising this value can help -CHANNEL_LAYER_RECEIVE_MAX_RETRY = 10 - -# Logging configuration. -LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'formatters': { - 'simple': { - 'format': '%(asctime)s %(levelname)-8s %(name)s %(message)s', - }, - # FIXME: you will need this logger to run inventory imports - # 'timed_import': { - # '()': 'awx.main.utils.formatters.TimeFormatter', - # 'format': '%(relativeSeconds)9.3f %(levelname)-8s %(message)s' - # } - }, - 'handlers': { - 'console': { - '()': 'logging.StreamHandler', - 'level': 'DEBUG', - 'formatter': 'simple', - }, - 'null': { - 'class': 'logging.NullHandler', - }, - # 'inventory_import': { - # 'level': 'DEBUG', - # 'class':'logging.StreamHandler', - # 'formatter': 'timed_import', - # }, - }, - 'loggers': { - 'django': { - 'handlers': ['console'], - }, - 'django.request': { - 'handlers': ['console'], - 'level': 'WARNING', - }, - 'rest_framework.request': { - 'handlers': ['console'], - 'level': 'WARNING', - 'propagate': False, - }, - 'py.warnings': { - 'handlers': ['console'], - }, - 'awx': { - 'handlers': ['console'], - 'level': 'DEBUG', - }, - # 'awx.main.commands.inventory_import': { - # 'handlers': ['inventory_import'], - # 'propagate': False - # }, - 'awx.main.consumers': { - 'handlers': ['null'] - }, - 'awx.main.access': { - 'handlers': ['null'], - 'propagate': False, - }, - 'awx.analytics': { - 'handlers': ['null'], - 'level': 'INFO', - 'propagate': False - }, - 'django_auth_ldap': { - 'handlers': ['console'], - 'level': 'DEBUG', - }, - 'social': { - 'handlers': ['console'], - 'level': 'DEBUG', - }, - } -} -# Apply coloring to messages logged to the console -COLOR_LOGS = False - -# https://github.com/django-polymorphic/django-polymorphic/issues/195 -# FIXME: Disabling models.E006 warning until we can renamed Project and InventorySource -SILENCED_SYSTEM_CHECKS = ['models.E006'] - -# Use middleware to get request statistics -AWX_REQUEST_PROFILE = False - -# ----- start settings from development.py ------ - - - -# awx-manage shell_plus --notebook -NOTEBOOK_ARGUMENTS = [ - '--NotebookApp.token=', - '--ip', '0.0.0.0', - '--port', '8888', - '--allow-root', - '--no-browser', -] - -# print SQL queries in shell_plus -SHELL_PLUS_PRINT_SQL = False - -# show colored logs in the dev environment -# to disable this, set `COLOR_LOGS = False` in awx/settings/local_settings.py -LOGGING['handlers']['console']['()'] = 'awx.main.utils.handlers.ColorHandler' -COLOR_LOGS = True - -ALLOWED_HOSTS = ['*'] - -mimetypes.add_type("image/svg+xml", ".svg", True) -mimetypes.add_type("image/svg+xml", ".svgz", True) - -# Disallow sending session cookies over insecure connections -SESSION_COOKIE_SECURE = False - -# Disallow sending csrf cookies over insecure connections -CSRF_COOKIE_SECURE = False - -# Override django.template.loaders.cached.Loader in defaults.py -template = next((tpl_backend for tpl_backend in TEMPLATES if tpl_backend['NAME'] == 'default'), None) # noqa -template['OPTIONS']['loaders'] = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', -) - -# Disable capturing all SQL queries when running celeryd in development. -if 'celery' in sys.argv: - SQL_DEBUG = False - -CELERYD_HIJACK_ROOT_LOGGER = False -CELERYD_LOG_COLOR = True - -CALLBACK_QUEUE = "callback_tasks" - -# Enable PROOT for tower-qa integration tests. -# Note: This setting may be overridden by database settings. -AWX_PROOT_ENABLED = True - -AWX_ISOLATED_USERNAME = 'root' -AWX_ISOLATED_CHECK_INTERVAL = 1 -AWX_ISOLATED_LAUNCH_TIMEOUT = 30 - -# Disable Pendo on the UI for development/test. -# Note: This setting may be overridden by database settings. -PENDO_TRACKING_STATE = "off" - -# Use Django-Jenkins if installed. Only run tests for awx.main app. -try: - import django_jenkins - INSTALLED_APPS += (django_jenkins.__name__,) - PROJECT_APPS = ('awx.main.tests', 'awx.api.tests',) -except ImportError: - pass - -if 'django_jenkins' in INSTALLED_APPS: - JENKINS_TASKS = ( - # 'django_jenkins.tasks.run_pylint', - # 'django_jenkins.tasks.run_flake8', - # The following are not needed when including run_flake8 - # 'django_jenkins.tasks.run_pep8', - # 'django_jenkins.tasks.run_pyflakes', - # The following are handled by various grunt tasks and no longer required - # 'django_jenkins.tasks.run_jshint', - # 'django_jenkins.tasks.run_csslint', - ) - PEP8_RCFILE = "setup.cfg" - PYLINT_RCFILE = ".pylintrc" - -INSTALLED_APPS += ('rest_framework_swagger',) - -# Much faster than the default -# https://docs.djangoproject.com/en/1.6/topics/auth/passwords/#how-django-stores-passwords -PASSWORD_HASHERS = ( - 'django.contrib.auth.hashers.MD5PasswordHasher', - 'django.contrib.auth.hashers.PBKDF2PasswordHasher', -) - -# Configure a default UUID for development only. -SYSTEM_UUID = '00000000-0000-0000-0000-000000000000' - -# Store a snapshot of default settings at this point before loading any -# customizable config files. -DEFAULTS_SNAPSHOT = {} -this_module = sys.modules[__name__] -for setting in dir(this_module): - if setting == setting.upper(): - DEFAULTS_SNAPSHOT[setting] = copy.deepcopy(getattr(this_module, setting)) - - -BASE_VENV_PATH = os.getenv('VIRTUAL_ENV', '/usr/local') -ANSIBLE_VENV_PATH = BASE_VENV_PATH -AWX_VENV_PATH = BASE_VENV_PATH - - -CLUSTER_HOST_ID = 'tower' - -# Supervisor service name dictionary used for programatic restart -SERVICE_NAME_DICT = { - "celery": "celery", - "callback": "receiver", - "runworker": "channels", - "uwsgi": "uwsgi", - "daphne": "daphne", - "nginx": "nginx"} -# Used for sending commands in automatic restart -# UWSGI_FIFO_LOCATION = '/awxfifo' - diff --git a/tests_awx/test_organization.py b/tests_awx/test_organization.py index d1ee492b..0f5e89c3 100644 --- a/tests_awx/test_organization.py +++ b/tests_awx/test_organization.py @@ -2,8 +2,27 @@ from awx.api.versioning import reverse +from tower_cli.conf import settings +from tower_cli import get_resource + +from awx.main.models import Organization + @pytest.mark.django_db def test_create_org(post, admin): url = reverse('api:organization_list') r = post(url, data={'name': 'anorg'}, user=admin, expect=201) + + +@pytest.fixture +def organization(): + return Organization.objects.create(name='an-org') + + +@pytest.mark.django_db +def test_read_org(organization, admin): + with settings.runtime_values(host='connection: local', username=admin.username): + org_res = get_resource('organization') + r = org_res.get(name='an-org') + assert r['name'] == organization.name + diff --git a/tower_cli/api.py b/tower_cli/api.py index 4f3b2c3d..9493c8fa 100644 --- a/tower_cli/api.py +++ b/tower_cli/api.py @@ -37,6 +37,63 @@ TOWER_DATETIME_FMT = r'%Y-%m-%dT%H:%M:%S.%fZ' +def local_request(method, url, **kwargs): + try: + # Django + from django.core.urlresolvers import resolve + from django.utils.six.moves.urllib.parse import urlparse + + from rest_framework.test import ( + APIRequestFactory, + force_authenticate, + ) + # test import + from awx.main.models import User + except ImportError: + logger.debug('You are using local connection, you need AWX installed.') + raise + + print (method, url, kwargs) + # content = kwargs['params'] + data = kwargs['params'] + middleware = None + # headers = content['headers'] + # # passwords? who needs em + user = User.objects.get(username=settings.username) + # if 'Content-Type' in headers: + # kwargs['content_type'] = headers['Content-Type'] + + # def rf(url, data_or_user=None, user=None, middleware=None, expect=None, **kwargs): + if 'format' not in kwargs and 'content_type' not in kwargs: + kwargs['format'] = 'json' + + view, view_args, view_kwargs = resolve(urlparse(url)[2]) + request = getattr(APIRequestFactory(), method.lower())(url, **kwargs) + if isinstance(kwargs.get('cookies', None), dict): + for key, value in kwargs['cookies'].items(): + request.COOKIES[key] = value + if middleware: + middleware.process_request(request) + if user: + force_authenticate(request, user=user) + + response = view(request, *view_args, **view_kwargs) + if middleware: + middleware.process_response(request, response) + if hasattr(response, 'render'): + response.render() + + # hacks specific to tower-cli + def make_json(self): + return self.data + + import types + response.json = types.MethodType(make_json, response) + + return response + + + class BasicTowerAuth(AuthBase): def __init__(self, username, password, cli_client): @@ -132,10 +189,15 @@ def _make_request(self, method, url, args, kwargs): # Call the superclass method. try: with warnings.catch_warnings(): - warnings.simplefilter( - "ignore", urllib3.exceptions.InsecureRequestWarning) - return super(Client, self).request( - method, url, *args, verify=verify_ssl, **kwargs) + if settings.host == 'connection: local': + return local_request( + method, url, *args, **kwargs + ) + else: + warnings.simplefilter( + "ignore", urllib3.exceptions.InsecureRequestWarning) + return super(Client, self).request( + method, url, *args, verify=verify_ssl, **kwargs) except SSLError as ex: # Throw error if verify_ssl not set to false and server # is not using verified certificate. From 12088a9108ee0cb9c50f1328232060910fa7d46b Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 14 Mar 2018 16:21:39 -0400 Subject: [PATCH 7/9] install click --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7ad33ebd..2c979fce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,10 +13,10 @@ matrix: install: - ./install_awx.sh - python install_awx_req.py - - pip install -r requirements_dev.txt + - pip install -r requirements_dev.txt -r requirements.txt script: - flake8 tower-cli/ tests/ - - tox + - tox tests/ - py.test tests_awx/ after_success: coveralls From 9f94211c24bc02bd66f310021a00677a5b2d383c Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 14 Mar 2018 17:01:17 -0400 Subject: [PATCH 8/9] thinking about cleaning things up --- tests_awx/conftest.py | 80 ++-------------------------------- tests_awx/test_organization.py | 15 +++---- tower_cli/api.py | 18 +++++--- 3 files changed, 21 insertions(+), 92 deletions(-) diff --git a/tests_awx/conftest.py b/tests_awx/conftest.py index 7e63c45e..37e57ce3 100644 --- a/tests_awx/conftest.py +++ b/tests_awx/conftest.py @@ -11,7 +11,7 @@ ) # AWX -from awx.main.models import User +from awx.main.models import User, Organization @pytest.fixture @@ -19,80 +19,6 @@ def admin(): return User.objects.create(username='admin_user', is_superuser=True) -def _request(verb): - def rf(url, data_or_user=None, user=None, middleware=None, expect=None, **kwargs): - if type(data_or_user) is User and user is None: - user = data_or_user - elif 'data' not in kwargs: - kwargs['data'] = data_or_user - if 'format' not in kwargs and 'content_type' not in kwargs: - kwargs['format'] = 'json' - - view, view_args, view_kwargs = resolve(urlparse(url)[2]) - request = getattr(APIRequestFactory(), verb)(url, **kwargs) - if isinstance(kwargs.get('cookies', None), dict): - for key, value in kwargs['cookies'].items(): - request.COOKIES[key] = value - if middleware: - middleware.process_request(request) - if user: - force_authenticate(request, user=user) - - response = view(request, *view_args, **view_kwargs) - if middleware: - middleware.process_response(request, response) - if expect: - if response.status_code != expect: - if getattr(response, 'data', None): - try: - data_copy = response.data.copy() - # Make translated strings printable - for key, value in response.data.items(): - if isinstance(value, list): - response.data[key] = [] - for item in value: - response.data[key].append(str(item)) - else: - response.data[key] = str(value) - except Exception: - response.data = data_copy - assert response.status_code == expect - if hasattr(response, 'render'): - response.render() - return response - return rf - - -@pytest.fixture -def post(): - return _request('post') - - -@pytest.fixture -def get(): - return _request('get') - - -@pytest.fixture -def put(): - return _request('put') - - -@pytest.fixture -def patch(): - return _request('patch') - - -@pytest.fixture -def delete(): - return _request('delete') - - -@pytest.fixture -def head(): - return _request('head') - - @pytest.fixture -def options(): - return _request('options') +def organization(): + return Organization.objects.create(name='an-org') diff --git a/tests_awx/test_organization.py b/tests_awx/test_organization.py index 0f5e89c3..cfaa288e 100644 --- a/tests_awx/test_organization.py +++ b/tests_awx/test_organization.py @@ -5,18 +5,13 @@ from tower_cli.conf import settings from tower_cli import get_resource -from awx.main.models import Organization - @pytest.mark.django_db -def test_create_org(post, admin): - url = reverse('api:organization_list') - r = post(url, data={'name': 'anorg'}, user=admin, expect=201) - - -@pytest.fixture -def organization(): - return Organization.objects.create(name='an-org') +def test_create_org(admin): + with settings.runtime_values(host='connection: local', username=admin.username): + org_res = get_resource('organization') + r = org_res.create(name='an-org-created') + assert r['name'] == 'an-org-created' @pytest.mark.django_db diff --git a/tower_cli/api.py b/tower_cli/api.py index 9493c8fa..83631484 100644 --- a/tower_cli/api.py +++ b/tower_cli/api.py @@ -53,10 +53,18 @@ def local_request(method, url, **kwargs): logger.debug('You are using local connection, you need AWX installed.') raise - print (method, url, kwargs) - # content = kwargs['params'] - data = kwargs['params'] + if 'data' in kwargs: + data = kwargs['data'] + if not isinstance(data, dict): + data = json.loads(data) + elif 'params' in kwargs: + data = kwargs['params'] + else: + data = {} + middleware = None + request_kwargs = {} + request_kwargs['data'] = data # headers = content['headers'] # # passwords? who needs em user = User.objects.get(username=settings.username) @@ -65,10 +73,10 @@ def local_request(method, url, **kwargs): # def rf(url, data_or_user=None, user=None, middleware=None, expect=None, **kwargs): if 'format' not in kwargs and 'content_type' not in kwargs: - kwargs['format'] = 'json' + request_kwargs['format'] = 'json' view, view_args, view_kwargs = resolve(urlparse(url)[2]) - request = getattr(APIRequestFactory(), method.lower())(url, **kwargs) + request = getattr(APIRequestFactory(), method.lower())(url, **request_kwargs) if isinstance(kwargs.get('cookies', None), dict): for key, value in kwargs['cookies'].items(): request.COOKIES[key] = value From f798073679f9f2e3b4db8861ce619548cacdce22 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 15 Mar 2018 09:24:34 -0400 Subject: [PATCH 9/9] restrict integration tests to python2 --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2c979fce..89918810 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,12 +11,12 @@ matrix: allow_failures: - python: nightly install: - - ./install_awx.sh - - python install_awx_req.py + - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then ./install_awx.sh; fi + - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then python install_awx_req.py; fi - pip install -r requirements_dev.txt -r requirements.txt script: - flake8 tower-cli/ tests/ - tox tests/ - - py.test tests_awx/ + - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then py.test tests_awx/; fi after_success: coveralls