Add tempest plugin
Added tempest plugin with ability to run new tempest tests for glare Change-Id: I7577ed3aa8f2bd79f57afeb6916851da9ea47f62
This commit is contained in:
parent
a2950da92f
commit
299c09a4cd
|
@ -0,0 +1,57 @@
|
||||||
|
# Copyright (c) 2015 Mirantis, Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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 tempest.common import credentials_factory as common_creds
|
||||||
|
from tempest import config
|
||||||
|
from tempest.lib import auth
|
||||||
|
|
||||||
|
|
||||||
|
from glare_tempest_plugin.services.artifacts import artifacts_client
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class Manager(object):
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
credentials=common_creds.get_configured_admin_credentials(
|
||||||
|
'identity_admin')):
|
||||||
|
self.auth_provider = get_auth_provider(credentials)
|
||||||
|
|
||||||
|
self.artifacts_client = artifacts_client.ArtifactsClient(
|
||||||
|
self.auth_provider)
|
||||||
|
|
||||||
|
|
||||||
|
def get_auth_provider(credentials, scope='project'):
|
||||||
|
default_params = {
|
||||||
|
'disable_ssl_certificate_validation':
|
||||||
|
CONF.identity.disable_ssl_certificate_validation,
|
||||||
|
'ca_certs': CONF.identity.ca_certificates_file,
|
||||||
|
'trace_requests': CONF.debug.trace_requests
|
||||||
|
}
|
||||||
|
|
||||||
|
if isinstance(credentials, auth.KeystoneV3Credentials):
|
||||||
|
auth_provider_class, auth_url = \
|
||||||
|
auth.KeystoneV3AuthProvider, CONF.identity.uri_v3
|
||||||
|
else:
|
||||||
|
auth_provider_class, auth_url = \
|
||||||
|
auth.KeystoneV2AuthProvider, CONF.identity.uri
|
||||||
|
|
||||||
|
_auth_provider = auth_provider_class(credentials, auth_url,
|
||||||
|
scope=scope,
|
||||||
|
**default_params)
|
||||||
|
_auth_provider.set_auth()
|
||||||
|
return _auth_provider
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Copyright 2016 Red Hat, Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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 oslo_config import cfg
|
||||||
|
|
||||||
|
service_available_group = cfg.OptGroup(name='service_available',
|
||||||
|
title='Available OpenStack Services')
|
||||||
|
|
||||||
|
ServiceAvailableGroup = [
|
||||||
|
cfg.BoolOpt("glare",
|
||||||
|
default=True,
|
||||||
|
help="Whether or not glare is expected to be available")
|
||||||
|
]
|
||||||
|
|
||||||
|
artifacts_group = cfg.OptGroup(name="artifacts",
|
||||||
|
title='Glare Options')
|
||||||
|
|
||||||
|
ArtifactGroup = [
|
||||||
|
cfg.StrOpt("catalog_type",
|
||||||
|
default="artifact"),
|
||||||
|
cfg.StrOpt("endpoint_type",
|
||||||
|
default="publicURL",
|
||||||
|
choices=["publicURL", "adminURL", "internalURL"],
|
||||||
|
help="The endpoint type for artifacts service")
|
||||||
|
]
|
|
@ -0,0 +1,56 @@
|
||||||
|
# Copyright 2016 Red Hat, Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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 os
|
||||||
|
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
from tempest import config
|
||||||
|
from tempest.test_discover import plugins
|
||||||
|
|
||||||
|
|
||||||
|
from glare_tempest_plugin import config as glare_config
|
||||||
|
|
||||||
|
|
||||||
|
class GlareTempestPlugin(plugins.TempestPlugin):
|
||||||
|
def load_tests(self):
|
||||||
|
base_path = os.path.split(os.path.dirname(
|
||||||
|
os.path.abspath(__file__)))[0]
|
||||||
|
test_dir = "glare_tempest_plugin/tests"
|
||||||
|
full_test_dir = os.path.join(base_path, test_dir)
|
||||||
|
return full_test_dir, base_path
|
||||||
|
|
||||||
|
def register_opts(self, conf):
|
||||||
|
try:
|
||||||
|
config.register_opt_group(
|
||||||
|
conf, glare_config.service_available_group,
|
||||||
|
glare_config.ServiceAvailableGroup
|
||||||
|
)
|
||||||
|
except cfg.DuplicateOptError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
config.register_opt_group(conf, glare_config.artifacts_group,
|
||||||
|
glare_config.ArtifactGroup)
|
||||||
|
except cfg.DuplicateOptError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_opt_lists(self):
|
||||||
|
return [
|
||||||
|
(glare_config.service_available_group.name,
|
||||||
|
glare_config.ServiceAvailableGroup),
|
||||||
|
(glare_config.artifacts_group.name,
|
||||||
|
glare_config.ArtifactGroup)
|
||||||
|
]
|
|
@ -0,0 +1,121 @@
|
||||||
|
# Copyright (c) 2016 Mirantis, Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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 json
|
||||||
|
|
||||||
|
from tempest import config
|
||||||
|
from tempest.lib.common import rest_client
|
||||||
|
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class ArtifactsClient(rest_client.RestClient):
|
||||||
|
|
||||||
|
def __init__(self, auth_provider):
|
||||||
|
super(ArtifactsClient, self).__init__(
|
||||||
|
auth_provider,
|
||||||
|
CONF.artifacts.catalog_type,
|
||||||
|
CONF.identity.region,
|
||||||
|
endpoint_type=CONF.artifacts.endpoint_type)
|
||||||
|
|
||||||
|
def create_artifact(self, type_name, name, version='0.0.0', **kwargs):
|
||||||
|
kwargs.update({'name': name, 'version': version})
|
||||||
|
uri = '/artifacts/{type_name}'.format(type_name=type_name)
|
||||||
|
resp, body = self.post(uri, body=json.dumps(kwargs))
|
||||||
|
self.expected_success(201, resp.status)
|
||||||
|
parsed = self._parse_resp(body)
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
def get_artifact(self, type_name, art_id):
|
||||||
|
uri = '/artifacts/{type_name}/{id}'.format(
|
||||||
|
type_name=type_name,
|
||||||
|
id=art_id)
|
||||||
|
resp, body = self.get(uri)
|
||||||
|
self.expected_success(200, resp.status)
|
||||||
|
parsed = self._parse_resp(body)
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
def update_artifact(self, type_name, art_id, remove_props=None, **kwargs):
|
||||||
|
headers = {'Content-Type': 'application/json-patch+json'}
|
||||||
|
uri = '/artifacts/{type_name}/{id}'.format(type_name=type_name,
|
||||||
|
id=art_id)
|
||||||
|
changes = []
|
||||||
|
|
||||||
|
if remove_props:
|
||||||
|
for prop_name in remove_props:
|
||||||
|
if prop_name not in kwargs:
|
||||||
|
if '/' in prop_name:
|
||||||
|
changes.append({'op': 'remove',
|
||||||
|
'path': '/%s' % prop_name})
|
||||||
|
else:
|
||||||
|
changes.append({'op': 'replace',
|
||||||
|
'path': '/%s' % prop_name,
|
||||||
|
'value': None})
|
||||||
|
for prop_name in kwargs:
|
||||||
|
changes.append({'op': 'add',
|
||||||
|
'path': '/%s' % prop_name,
|
||||||
|
'value': kwargs[prop_name]})
|
||||||
|
resp, body = self.patch(uri, json.dumps(changes), headers=headers)
|
||||||
|
self.expected_success(200, resp.status)
|
||||||
|
parsed = self._parse_resp(body)
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
def activate_artifact(self, type_name, art_id):
|
||||||
|
return self.update_artifact(type_name, art_id, status='active')
|
||||||
|
|
||||||
|
def deactivate_artifact(self, type_name, art_id):
|
||||||
|
return self.update_artifact(type_name, art_id, status='deactivated')
|
||||||
|
|
||||||
|
def reactivate_artifact(self, type_name, art_id):
|
||||||
|
return self.update_artifact(type_name, art_id, status='active')
|
||||||
|
|
||||||
|
def publish_artifact(self, type_name, art_id):
|
||||||
|
return self.update_artifact(type_name, art_id, visibility='public')
|
||||||
|
|
||||||
|
def upload_blob(self, type_name, art_id, blob_property, data):
|
||||||
|
headers = {'Content-Type': 'application/octet-stream'}
|
||||||
|
uri = '/artifacts/{type_name}/{id}/{blob_prop}'.format(
|
||||||
|
type_name=type_name,
|
||||||
|
id=art_id,
|
||||||
|
blob_prop=blob_property)
|
||||||
|
resp, body = self.put(uri, data, headers=headers)
|
||||||
|
self.expected_success(200, resp.status)
|
||||||
|
parsed = self._parse_resp(body)
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
def download_blob(self, type_name, art_id, blob_property):
|
||||||
|
uri = '/artifacts/{type_name}/{id}/{blob_prop}'.format(
|
||||||
|
type_name=type_name,
|
||||||
|
id=art_id,
|
||||||
|
blob_prop=blob_property)
|
||||||
|
resp, body = self.get(uri)
|
||||||
|
self.expected_success(200, resp.status)
|
||||||
|
parsed = self._parse_resp(body)
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
def delete_artifact(self, type_name, art_id):
|
||||||
|
uri = '/artifacts/{type_name}/{id}'.format(
|
||||||
|
type_name=type_name,
|
||||||
|
id=art_id)
|
||||||
|
self.delete(uri)
|
||||||
|
|
||||||
|
def list_artifacts(self, type_name):
|
||||||
|
uri = '/artifacts/{}'.format(type_name)
|
||||||
|
resp, body = self.get(uri)
|
||||||
|
self.expected_success(200, resp.status)
|
||||||
|
parsed = self._parse_resp(body)
|
||||||
|
return parsed
|
|
@ -0,0 +1,81 @@
|
||||||
|
# Copyright 2016 Red Hat, Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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 glare_tempest_plugin import clients
|
||||||
|
from tempest.common import credentials_factory as common_creds
|
||||||
|
from tempest.common import dynamic_creds
|
||||||
|
from tempest import config
|
||||||
|
from tempest.lib import base
|
||||||
|
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class BaseArtifactTest(base.BaseTestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(BaseArtifactTest, cls).setUpClass()
|
||||||
|
cls.resource_setup()
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_client_with_isolated_creds(cls, type_of_creds="admin"):
|
||||||
|
creds = cls.get_configured_isolated_creds(
|
||||||
|
type_of_creds=type_of_creds)
|
||||||
|
|
||||||
|
os = clients.Manager(credentials=creds)
|
||||||
|
client = os.artifact_client
|
||||||
|
return client
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def resource_setup(cls):
|
||||||
|
if not CONF.service_available.glare:
|
||||||
|
skip_msg = "Glare is disabled"
|
||||||
|
raise cls.skipException(skip_msg)
|
||||||
|
if not hasattr(cls, "os"):
|
||||||
|
creds = cls.get_configured_isolated_creds(
|
||||||
|
type_of_creds='primary')
|
||||||
|
cls.os = clients.Manager(credentials=creds)
|
||||||
|
cls.artifacts_client = cls.os.artifacts_client
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_configured_isolated_creds(cls, type_of_creds='admin'):
|
||||||
|
identity_version = CONF.identity.auth_version
|
||||||
|
if identity_version == 'v3':
|
||||||
|
cls.admin_role = CONF.identity.admin_role
|
||||||
|
else:
|
||||||
|
cls.admin_role = 'admin'
|
||||||
|
cls.dynamic_cred = dynamic_creds.DynamicCredentialProvider(
|
||||||
|
identity_version=CONF.identity.auth_version,
|
||||||
|
name=cls.__name__, admin_role=cls.admin_role,
|
||||||
|
admin_creds=common_creds.get_configured_admin_credentials(
|
||||||
|
'identity_admin'))
|
||||||
|
if type_of_creds == 'primary':
|
||||||
|
creds = cls.dynamic_cred.get_primary_creds()
|
||||||
|
elif type_of_creds == 'admin':
|
||||||
|
creds = cls.dynamic_cred.get_admin_creds()
|
||||||
|
elif type_of_creds == 'alt':
|
||||||
|
creds = cls.dynamic_cred.get_alt_creds()
|
||||||
|
else:
|
||||||
|
creds = cls.dynamic_cred.get_credentials(type_of_creds)
|
||||||
|
cls.dynamic_cred.type_of_creds = type_of_creds
|
||||||
|
|
||||||
|
return creds.credentials
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Copyright 2016 Red Hat, Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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 testtools
|
||||||
|
|
||||||
|
|
||||||
|
from glare_tempest_plugin.tests.api import base
|
||||||
|
from tempest import config
|
||||||
|
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class TestListSanity(base.BaseArtifactTest):
|
||||||
|
|
||||||
|
@testtools.testcase.attr('smoke')
|
||||||
|
def test_list_artifacts(self):
|
||||||
|
art = self.artifacts_client.create_artifact('images', 'tempest_test')
|
||||||
|
self.artifacts_client.list_artifacts('images')
|
||||||
|
self.artifacts_client.get_artifact('images', art['id'])
|
||||||
|
self.artifacts_client.update_artifact(type_name='images',
|
||||||
|
art_id=art['id'],
|
||||||
|
name='newnewname')
|
||||||
|
data = 'dataaaa'
|
||||||
|
self.artifacts_client.upload_blob('images', art['id'], 'image', data)
|
||||||
|
self.artifacts_client.download_blob('images', art['id'], 'image')
|
||||||
|
self.artifacts_client.delete_artifact('images', art['id'])
|
|
@ -21,6 +21,8 @@ classifier =
|
||||||
[files]
|
[files]
|
||||||
packages =
|
packages =
|
||||||
glare
|
glare
|
||||||
|
glare_tempest_plugin
|
||||||
|
|
||||||
data_files =
|
data_files =
|
||||||
etc/glare =
|
etc/glare =
|
||||||
etc/glare-paste.ini
|
etc/glare-paste.ini
|
||||||
|
@ -34,9 +36,10 @@ oslo.config.opts =
|
||||||
glare = glare.opts:list_artifacts_opts
|
glare = glare.opts:list_artifacts_opts
|
||||||
oslo.policy.enforcer =
|
oslo.policy.enforcer =
|
||||||
glare = glare.common.policy:_get_enforcer
|
glare = glare.common.policy:_get_enforcer
|
||||||
|
|
||||||
oslo.policy.policies =
|
oslo.policy.policies =
|
||||||
glare = glare.common.policy:list_rules
|
glare = glare.common.policy:list_rules
|
||||||
|
tempest.test_plugins =
|
||||||
|
glare_tempest_tests = glare_tempest_plugin.plugin:GlareTempestPlugin
|
||||||
|
|
||||||
[build_sphinx]
|
[build_sphinx]
|
||||||
all_files = 1
|
all_files = 1
|
||||||
|
|
Loading…
Reference in New Issue