Skip to content

Commit

Permalink
Initialize functional testing (#30)
Browse files Browse the repository at this point in the history
Initialize functional testing

Reviewed-by: None <None>
Reviewed-by: kucerakk <kucerakk@gmail.com>
Reviewed-by: Artem Goncharov <Artem.goncharov@gmail.com>
Reviewed-by: OpenTelekomCloud Bot <None>
  • Loading branch information
gtema authored Jul 20, 2021
1 parent 45896c7 commit 49b390c
Show file tree
Hide file tree
Showing 22 changed files with 325 additions and 75 deletions.
8 changes: 8 additions & 0 deletions .zuul.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,16 @@
- otc-tox-pep8
- otc-tox-py39-tips
- octavia-proxy-build-image
- tox-functional:
required-projects:
- name: opentelekomcloud/python-otcextensions
override-checkout: elb
gate:
jobs:
- otc-tox-pep8
- otc-tox-py39-tips
- octavia-proxy-upload-image
- tox-functional:
required-projects:
- name: opentelekomcloud/python-otcextensions
override-checkout: elb
3 changes: 2 additions & 1 deletion doc/source/contributor/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ Contributor Guidelines
Developer Setup
---------------

- tox create venv for octavia-proxy
- tox `tox -e py39 --notest`
- source into it
- `rm -rf .tox/py39/lib/python3.9/site-packages/otcextensions*`
- with the venv python go to otcextensions elb branch and do
`python setup.py develop`
- add into the clouds.yaml
Expand Down
2 changes: 1 addition & 1 deletion etc/octavia_proxy.conf
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ auth_strategy = validatetoken
# Dictionary of enabled provider driver names and descriptions
# A comma separated list of dictionaries of the enabled provider driver names
# and descriptions.
enabled_provider_drivers = elbv3:The Open Telekom Cloud AZ aware LD driver.
enabled_provider_drivers = elbv3:The Open Telekom Cloud AZ aware LB driver.
#enabled_provider_drivers = elbv2:The Open Telekom Cloud Enhanced LB driver.,elbv3:The Open Telekom Cloud AZ aware LD driver.
#

Expand Down
23 changes: 0 additions & 23 deletions etc/policy.yaml

This file was deleted.

5 changes: 3 additions & 2 deletions octavia_proxy/api/common/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ class ContextHook(hooks.PecanHook):

def on_route(self, state):
context_obj = context.Context.from_environ(state.request.environ)
token_info = state.request.environ['keystone.token_info']
context_obj.set_token_info(token_info)
token_info = state.request.environ.get('keystone.token_info')
if token_info:
context_obj.set_token_info(token_info)
state.request.context['octavia_context'] = context_obj


Expand Down
3 changes: 2 additions & 1 deletion octavia_proxy/api/v2/controllers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from octavia_proxy.api.v2.controllers import flavors
from octavia_proxy.api.v2.controllers import load_balancer
from octavia_proxy.api.v2.controllers import listener
from octavia_proxy.api.v2.controllers import pool
from octavia_proxy.api.v2.controllers import provider


Expand All @@ -20,7 +21,7 @@ def __init__(self):
super().__init__()
self.loadbalancers = load_balancer.LoadBalancersController()
self.listeners = listener.ListenersController()
# self.pools = pool.PoolsController()
self.pools = pool.PoolsController()
# self.l7policies = l7policy.L7PolicyController()
# self.healthmonitors = health_monitor.HealthMonitorController()
# self.quotas = quotas.QuotasController()
Expand Down
6 changes: 6 additions & 0 deletions octavia_proxy/api/v2/controllers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,9 @@ def _filter_fields(self, object_list, fields):
if member not in fields:
setattr(obj, member, wtypes.Unset)
return object_list

@staticmethod
def _get_attrs(obj):
attrs = [attr for attr in dir(obj) if not callable(
getattr(obj, attr)) and not attr.startswith("_")]
return attrs
9 changes: 0 additions & 9 deletions octavia_proxy/common/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,8 @@ class Context(common_context.RequestContext):
_session = None

def __init__(self, user_id=None, project_id=None, **kwargs):
if project_id:
kwargs['tenant'] = project_id

super().__init__(**kwargs)

self.is_admin = False

def set_token_info(self, token_info):
"""Set token into to be able to recreate session
Expand All @@ -51,7 +46,3 @@ def session(self):
vendor_hook='otcextensions.sdk:load')
self._session = sdk
return self._session

@property
def project_id(self):
return self.tenant
28 changes: 0 additions & 28 deletions octavia_proxy/common/policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,6 @@ def authorize(self, action, target, context, do_raise=True, exc=None):
do_raise is False.
"""
credentials = context.to_policy_values()
# Inject is_admin into the credentials to allow override via
# config auth_strategy = constants.NOAUTH
credentials['is_admin'] = (
credentials.get('is_admin') or context.is_admin)

if not exc:
exc = exceptions.PolicyForbidden
Expand All @@ -120,34 +116,10 @@ def authorize(self, action, target, context, do_raise=True, exc=None):
{'action': action, 'credentials': credentials})
return None

def check_is_admin(self, context):
"""Does roles contains 'admin' role according to policy setting.
"""
credentials = context.to_dict()
return self.enforce('context_is_admin', credentials, credentials)

def get_rules(self):
return self.rules


@oslo_policy.register('is_admin')
class IsAdminCheck(oslo_policy.Check):
"""An explicit check for is_admin."""

def __init__(self, kind, match):
"""Initialize the check."""

self.expected = match.lower() == 'true'

super().__init__(kind, str(self.expected))

def __call__(self, target, creds, enforcer):
"""Determine whether is_admin matches the requested value."""

return creds['is_admin'] == self.expected


# This is used for the oslopolicy-policy-generator tool
def get_no_context_enforcer():
return Policy()
7 changes: 6 additions & 1 deletion octavia_proxy/common/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from pathlib import Path

from oslo_config import cfg

Expand All @@ -20,8 +21,12 @@
def prepare_service(argv=None):
"""Sets global config from config file and sets up logging."""
argv = argv or []
config_file = '/etc/octavia_proxy/octavia_proxy.conf'
kwargs = dict()
if Path(config_file).is_file():
kwargs['default_config_files'] = [config_file]
config.init(
argv[1:],
default_config_files=['/etc/octavia_proxy/octavia_proxy.conf']
**kwargs
)
config.setup_logging(cfg.CONF)
10 changes: 1 addition & 9 deletions octavia_proxy/policies/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,10 @@
# role:admin
# User is admin to all APIs

policy.RuleDefault('context_is_admin',
'role:admin or role:load-balancer_admin'),

# Note: 'is_admin:True' is a policy rule that takes into account the
# auth_strategy == noauth configuration setting.
# It is equivalent to 'rule:context_is_admin or {auth_strategy == noauth}'

policy.RuleDefault('load-balancer:owner', 'project_id:%(project_id)s'),

# API access roles
policy.RuleDefault('load-balancer:admin', 'is_admin:True or '
'role:admin or '
policy.RuleDefault('load-balancer:admin', 'role:te_admin or '
'role:load-balancer_admin'),

policy.RuleDefault('load-balancer:observer_and_owner',
Expand Down
Empty file.
Empty file.
37 changes: 37 additions & 0 deletions octavia_proxy/tests/functional/api/test_root_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from octavia_proxy.tests.functional import base
import pecan.testing

from octavia_proxy.api import config as pconfig


class TestRootController(base.TestCase):
def get(self, app, path, params=None, headers=None, status=200,
expect_errors=False):
response = app.get(
path, params=params, headers=headers, status=status,
expect_errors=expect_errors)
return response

def _get_versions_with_config(self):
# Note: we need to set argv=() to stop the wsgi setup_app from
# pulling in the testing tool sys.argv
app = pecan.testing.load_test_app({'app': pconfig.app,
'wsme': pconfig.wsme}, argv=())
return self.get(app=app, path='/').json.get('versions', None)

def test_api_versions(self):
versions = self._get_versions_with_config()
version_ids = tuple(v.get('id') for v in versions)
self.assertIn('v2.0', version_ids)
Empty file.
147 changes: 147 additions & 0 deletions octavia_proxy/tests/functional/api/v2/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import openstack

from octavia_proxy.common import constants
from octavia_proxy.tests.functional import base
import pecan.testing

from octavia_proxy.api import config as pconfig


class BaseAPITest(base.TestCase):

BASE_PATH = '/v2'
BASE_PATH_v2_0 = '/v2.0'

# /lbaas/flavors
FLAVORS_PATH = '/flavors'
FLAVOR_PATH = FLAVORS_PATH + '/{flavor_id}'

# /lbaas/flavorprofiles
FPS_PATH = '/flavorprofiles'
FP_PATH = FPS_PATH + '/{fp_id}'

# /lbaas/availabilityzones
AZS_PATH = '/availabilityzones'
AZ_PATH = AZS_PATH + '/{az_name}'

# /lbaas/availabilityzoneprofiles
AZPS_PATH = '/availabilityzoneprofiles'
AZP_PATH = AZPS_PATH + '/{azp_id}'

# /lbaas/loadbalancers
LBS_PATH = '/lbaas/loadbalancers'
LB_PATH = LBS_PATH + '/{lb_id}'
LB_STATUS_PATH = LB_PATH + '/statuses'
LB_STATS_PATH = LB_PATH + '/stats'

# /lbaas/listeners/
LISTENERS_PATH = '/lbaas/listeners'
LISTENER_PATH = LISTENERS_PATH + '/{listener_id}'
LISTENER_STATS_PATH = LISTENER_PATH + '/stats'

# /lbaas/pools
POOLS_PATH = '/lbaas/pools'
POOL_PATH = POOLS_PATH + '/{pool_id}'

# /lbaas/pools/{pool_id}/members
MEMBERS_PATH = POOL_PATH + '/members'
MEMBER_PATH = MEMBERS_PATH + '/{member_id}'

# /lbaas/healthmonitors
HMS_PATH = '/lbaas/healthmonitors'
HM_PATH = HMS_PATH + '/{healthmonitor_id}'

# /lbaas/l7policies
L7POLICIES_PATH = '/lbaas/l7policies'
L7POLICY_PATH = L7POLICIES_PATH + '/{l7policy_id}'
L7RULES_PATH = L7POLICY_PATH + '/rules'
L7RULE_PATH = L7RULES_PATH + '/{l7rule_id}'

QUOTAS_PATH = '/lbaas/quotas'
QUOTA_PATH = QUOTAS_PATH + '/{project_id}'
QUOTA_DEFAULT_PATH = QUOTAS_PATH + '/{project_id}/default'

PROVIDERS_PATH = '/lbaas/providers'
FLAVOR_CAPABILITIES_PATH = (
PROVIDERS_PATH + '/{provider}/flavor_capabilities')
AVAILABILITY_ZONE_CAPABILITIES_PATH = (
PROVIDERS_PATH + '/{provider}/availability_zone_capabilities')

NOT_AUTHORIZED_BODY = {
'debuginfo': None, 'faultcode': 'Client',
'faultstring': 'Policy does not allow this request to be performed.'}

def setUp(self):
super().setUp()
self._token = None
self._sdk_connection = None
self.project_id = None
self.conf.config(
group='api_settings',
auth_strategy=constants.KEYSTONE_EXT)
self.app = self._make_app()

def reset_pecan():
pecan.set_config({}, overwrite=True)

self.addCleanup(reset_pecan)

def tearDown(self):
if self._sdk_connection:
self._sdk_connection.close()
super().tearDown()

def _make_app(self):
# Note: we need to set argv=() to stop the wsgi setup_app from
# pulling in the testing tool sys.argv
return pecan.testing.load_test_app(
{
'app': pconfig.app,
'wsme': pconfig.wsme,
'debug': True,
}, argv=())

def _get_full_path(self, path):
return ''.join([self.BASE_PATH, path])

def _get_full_path_v2_0(self, path):
return ''.join([self.BASE_PATH_v2_0, path])

def _build_body(self, json):
return {self.root_tag: json}

def _get_token(self):
if not self._sdk_connection:
self._sdk_connection = openstack.connect()
if not self._token:
self._token = self._sdk_connection.auth_token
self.project_id = self._sdk_connection.current_project_id
return self._token

def get(self, path, params=None, headers=None, status=200,
expect_errors=False, authorized=True):
full_path = self._get_full_path(path)
if authorized:
if not headers:
headers = dict()
headers['X-Auth-Token'] = self._get_token()
response = self.app.get(
full_path,
params=params,
headers=headers,
status=status,
expect_errors=expect_errors
)
return response
Loading

0 comments on commit 49b390c

Please sign in to comment.