Merge "Client support for Require section in manifest"
This commit is contained in:
commit
51058b6238
|
@ -15,14 +15,17 @@
|
|||
|
||||
from __future__ import print_function
|
||||
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import StringIO
|
||||
import sys
|
||||
import tempfile
|
||||
import textwrap
|
||||
import types
|
||||
import urlparse
|
||||
import uuid
|
||||
import zipfile
|
||||
|
||||
from oslo.serialization import jsonutils
|
||||
from oslo.utils import encodeutils
|
||||
|
@ -36,6 +39,8 @@ import yaql.exceptions
|
|||
|
||||
from muranoclient.common import exceptions
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Decorator for cli-args
|
||||
def arg(*args, **kwargs):
|
||||
|
@ -250,6 +255,47 @@ class Package(FileWrapperMixin):
|
|||
file_obj = File(file_obj)
|
||||
return Package(file_obj)
|
||||
|
||||
@property
|
||||
def manifest(self):
|
||||
"""Parsed manifest file of a package."""
|
||||
if not hasattr(self, '_manifest'):
|
||||
try:
|
||||
self._file.seek(0)
|
||||
zip_obj = zipfile.ZipFile(
|
||||
StringIO.StringIO(self._file.read()))
|
||||
self._manifest = yaml.safe_load(zip_obj.open('manifest.yaml'))
|
||||
except (zipfile.BadZipfile, KeyError, yaml.error.YAMLError) as e:
|
||||
LOG.exception("An error occurred,"
|
||||
" while extracting manifest from package")
|
||||
raise ValueError(e)
|
||||
return self._manifest
|
||||
|
||||
def requirements(self, base_url, dep_dict=None):
|
||||
"""Recursively scan Require section of manifests of all the
|
||||
dependencies. Returns a dict with FQPNs as keys and respective
|
||||
PackageFiles as values
|
||||
"""
|
||||
if not dep_dict:
|
||||
dep_dict = {}
|
||||
dep_dict[self.manifest['FullName']] = self
|
||||
if 'Require' in self.manifest:
|
||||
for dep_name, ver in self.manifest['Require'].iteritems():
|
||||
if dep_name in dep_dict:
|
||||
continue
|
||||
try:
|
||||
dep_url = to_url(dep_name, base_url, version=ver,
|
||||
path='/apps/', extension='.zip')
|
||||
req_file = Package.fromFile(dep_url)
|
||||
|
||||
dep_dict.update(req_file.requirements(
|
||||
base_url=base_url, dep_dict=dep_dict))
|
||||
except Exception:
|
||||
LOG.exception("Error occured during parsing dependecies "
|
||||
"of {0} requirement".format(
|
||||
self.manifest['FullName']))
|
||||
continue
|
||||
return dep_dict
|
||||
|
||||
|
||||
class Bundle(FileWrapperMixin):
|
||||
"""Represents murano bundle contents."""
|
||||
|
@ -269,7 +315,7 @@ class Bundle(FileWrapperMixin):
|
|||
pass
|
||||
if bundle is None:
|
||||
try:
|
||||
bundle = yaml.load(self._file)
|
||||
bundle = yaml.safe_load(self._file)
|
||||
except yaml.error.YAMLError:
|
||||
pass
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import os
|
||||
import re
|
||||
import StringIO
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
|
@ -23,6 +24,7 @@ import requests
|
|||
import six
|
||||
from testtools import matchers
|
||||
|
||||
from muranoclient.common import utils
|
||||
from muranoclient.openstack.common.apiclient import exceptions
|
||||
import muranoclient.shell
|
||||
from muranoclient.tests import base
|
||||
|
@ -281,13 +283,16 @@ class ShellPackagesOperations(ShellTest):
|
|||
args.categories = ['Cat1', 'Cat2 with space']
|
||||
args.is_public = True
|
||||
|
||||
v1_shell.do_package_import(self.client, args)
|
||||
result = {RESULT_PACKAGE: utils.Package.fromFile(
|
||||
StringIO.StringIO("123"))}
|
||||
with mock.patch(
|
||||
'muranoclient.common.utils.Package.requirements',
|
||||
mock.Mock(side_effect=lambda *args, **kwargs: result)):
|
||||
v1_shell.do_package_import(self.client, args)
|
||||
|
||||
self.client.packages.create.assert_called_once_with(
|
||||
{'categories': ['Cat1', 'Cat2 with space'], 'is_public': True},
|
||||
((RESULT_PACKAGE, mock.ANY),),
|
||||
murano_repo_url=args.murano_repo_url,
|
||||
version=args.version,
|
||||
{RESULT_PACKAGE: mock.ANY},
|
||||
)
|
||||
|
||||
def test_package_import_no_categories(self):
|
||||
|
@ -299,13 +304,17 @@ class ShellPackagesOperations(ShellTest):
|
|||
args.categories = None
|
||||
args.is_public = False
|
||||
|
||||
v1_shell.do_package_import(self.client, args)
|
||||
result = {RESULT_PACKAGE: utils.Package.fromFile(
|
||||
StringIO.StringIO("123"))}
|
||||
|
||||
with mock.patch(
|
||||
'muranoclient.common.utils.Package.requirements',
|
||||
mock.Mock(side_effect=lambda *args, **kwargs: result)):
|
||||
v1_shell.do_package_import(self.client, args)
|
||||
|
||||
self.client.packages.create.assert_called_once_with(
|
||||
{'is_public': False},
|
||||
((RESULT_PACKAGE, mock.ANY),),
|
||||
murano_repo_url=args.murano_repo_url,
|
||||
version=args.version,
|
||||
{RESULT_PACKAGE: mock.ANY},
|
||||
)
|
||||
|
||||
def test_package_import_url(self):
|
||||
|
@ -317,21 +326,24 @@ class ShellPackagesOperations(ShellTest):
|
|||
|
||||
resp = requests.Response()
|
||||
resp.status_code = 200
|
||||
resp.raw = True
|
||||
resp.raw = StringIO.StringIO("123")
|
||||
result = {args.filename: utils.Package.fromFile(
|
||||
StringIO.StringIO("123"))}
|
||||
with mock.patch(
|
||||
'requests.get',
|
||||
mock.Mock(side_effect=lambda k, *args, **kwargs: resp)):
|
||||
with mock.patch(
|
||||
'muranoclient.common.utils.Package.requirements',
|
||||
mock.Mock(side_effect=lambda *args, **kwargs: result)):
|
||||
|
||||
v1_shell.do_package_import(self.client, args)
|
||||
v1_shell.do_package_import(self.client, args)
|
||||
|
||||
self.client.packages.create.assert_called_once_with(
|
||||
{'is_public': False},
|
||||
((args.filename, mock.ANY),),
|
||||
murano_repo_url=args.murano_repo_url,
|
||||
version=args.version,
|
||||
{args.filename: mock.ANY},
|
||||
)
|
||||
|
||||
def test_package_import_fqpn(self):
|
||||
def test_package_import_by_name(self):
|
||||
args = TestArgs()
|
||||
|
||||
args.filename = "io.test.apps.test_application"
|
||||
|
@ -341,17 +353,19 @@ class ShellPackagesOperations(ShellTest):
|
|||
|
||||
resp = requests.Response()
|
||||
resp.status_code = 200
|
||||
resp.raw = True
|
||||
resp.raw = StringIO.StringIO("123")
|
||||
result = {args.filename: utils.Package.fromFile(
|
||||
StringIO.StringIO("123"))}
|
||||
with mock.patch(
|
||||
'requests.get',
|
||||
mock.Mock(side_effect=lambda k, *args, **kwargs: resp)):
|
||||
|
||||
v1_shell.do_package_import(self.client, args)
|
||||
with mock.patch(
|
||||
'muranoclient.common.utils.Package.requirements',
|
||||
mock.Mock(side_effect=lambda *args, **kwargs: result)):
|
||||
v1_shell.do_package_import(self.client, args)
|
||||
|
||||
self.assertTrue(self.client.packages.create.called)
|
||||
self.client.packages.create.assert_called_once_with(
|
||||
{'is_public': False},
|
||||
((args.filename, mock.ANY),),
|
||||
murano_repo_url=args.murano_repo_url,
|
||||
version=args.version,
|
||||
{args.filename: mock.ANY},
|
||||
)
|
||||
|
|
|
@ -12,19 +12,20 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import urllib
|
||||
|
||||
from oslo.serialization import jsonutils
|
||||
import requests
|
||||
import six
|
||||
import yaml
|
||||
|
||||
from muranoclient.common import base
|
||||
from muranoclient.common import exceptions
|
||||
from muranoclient.common import http
|
||||
from muranoclient.common import utils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_PAGE_SIZE = 20
|
||||
|
||||
|
||||
|
@ -49,27 +50,13 @@ class Category(base.Resource):
|
|||
|
||||
class PackageManager(base.Manager):
|
||||
resource_class = Package
|
||||
_tracked_packages = set()
|
||||
|
||||
def categories(self):
|
||||
return self._list('/v1/catalog/packages/categories',
|
||||
response_key='categories', obj_class=Category)
|
||||
|
||||
def create(self, data, files, version='', murano_repo_url=None):
|
||||
|
||||
files = requests.utils.to_key_val_list(files)
|
||||
if not files:
|
||||
raise ValueError("No files to import")
|
||||
name, file_name = files[0]
|
||||
if isinstance(file_name, six.string_types):
|
||||
file_name = utils.to_url(
|
||||
file_name,
|
||||
base_url=murano_repo_url,
|
||||
version=version,
|
||||
path='/apps/',
|
||||
extension='.zip')
|
||||
package = utils.Package.fromFile(file_name)
|
||||
|
||||
files = [(name, package.file(), )]
|
||||
def create(self, data, files):
|
||||
data = {'data': jsonutils.dumps(data)}
|
||||
url = '{0}/v1/catalog/packages'.format(self.api.endpoint)
|
||||
headers = {'X-Auth-Token': self.api.auth_token}
|
||||
|
|
|
@ -193,7 +193,7 @@ def do_package_delete(mc, args):
|
|||
help='Make package available for user from other tenants')
|
||||
@utils.arg('--version', default='',
|
||||
help='Version of the package to use from repository')
|
||||
def do_package_import(mc, args, list_packages=True):
|
||||
def do_package_import(mc, args):
|
||||
"""Import a package.
|
||||
`FILE` can be either a path to a zip file, url or a FQPN.
|
||||
`categories` could be separated by a comma
|
||||
|
@ -206,12 +206,23 @@ def do_package_import(mc, args, list_packages=True):
|
|||
filename = args.filename
|
||||
if os.path.isfile(filename):
|
||||
filename = open(filename, 'rb')
|
||||
|
||||
mc.packages.create(data, ((args.filename, filename),),
|
||||
version=args.version,
|
||||
murano_repo_url=args.murano_repo_url)
|
||||
if list_packages:
|
||||
do_package_list(mc)
|
||||
else:
|
||||
filename = utils.to_url(
|
||||
filename,
|
||||
version=args.version,
|
||||
base_url=args.murano_repo_url,
|
||||
extension='.zip',
|
||||
path='apps/',
|
||||
)
|
||||
package = utils.Package.fromFile(filename)
|
||||
reqs = package.requirements(base_url=args.murano_repo_url)
|
||||
for name, package in reqs.iteritems():
|
||||
try:
|
||||
mc.packages.create(data, {name: package.file()})
|
||||
except Exception as e:
|
||||
print("Error {0} occurred while installing package {1}".format(
|
||||
e, name))
|
||||
do_package_list(mc)
|
||||
|
||||
|
||||
@utils.arg('filename', metavar='<FILE>',
|
||||
|
@ -230,15 +241,26 @@ def do_bundle_import(mc, args):
|
|||
|
||||
data = {"is_public": args.is_public}
|
||||
|
||||
for package in bundle_file.packages():
|
||||
for package_info in bundle_file.packages():
|
||||
try:
|
||||
mc.packages.create(data, ((package['Name'], package['Name']),),
|
||||
version=package.get('Version'),
|
||||
murano_repo_url=args.murano_repo_url)
|
||||
package = utils.Package.fromFile(
|
||||
utils.to_url(
|
||||
package_info['Name'],
|
||||
version=package_info.get('Version'),
|
||||
base_url=args.murano_repo_url,
|
||||
extension='.zip',
|
||||
path='apps/',
|
||||
))
|
||||
except Exception as e:
|
||||
print("Error '{0}' occurred during import of {1} package".format(
|
||||
e, package['Name']))
|
||||
|
||||
print("Error {0} occurred while "
|
||||
"parsing package {1}".format(e, package_info['Name']))
|
||||
reqs = package.requirements(base_url=args.murano_repo_url)
|
||||
for name, package in reqs.iteritems():
|
||||
try:
|
||||
mc.packages.create(data, {name: package.file()})
|
||||
except Exception as e:
|
||||
print("Error {0} occurred while "
|
||||
"installing package {1}".format(e, name))
|
||||
do_package_list(mc)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue