Add package create to openstack CLI
usage: openstack package create [-h] [-t <HEAT_TEMPLATE>] [--classes-dir <CLASSES_DIRECTORY>] [-r <RESOURCES_DIRECTORY>] [-n <DISPLAY_NAME>] [--full-name <full-name>] [-a <AUTHOR>] [--tags [<TAG1 TAG2> [<TAG1 TAG2> ...]]] [-d <DESCRIPTION>] [-o <PACKAGE_NAME>] [-u <UI_DEFINITION>] [--type <TYPE>] [-l <LOGO>] Create an application package. Partially implements: blueprint openstack-client-plugin-support Change-Id: I3d81540ede601fe96952dbda483b792924a6fac4
This commit is contained in:
parent
f7bafa1b80
commit
85d360873e
138
muranoclient/osc/v1/package.py
Normal file
138
muranoclient/osc/v1/package.py
Normal file
@ -0,0 +1,138 @@
|
||||
# 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.
|
||||
|
||||
"""Application-catalog v1 package action implementation"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import zipfile
|
||||
|
||||
from muranoclient.v1.package_creator import hot_package
|
||||
from muranoclient.v1.package_creator import mpl_package
|
||||
from osc_lib.command import command
|
||||
from osc_lib import exceptions as exc
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CreatePackage(command.Command):
|
||||
"""Create an application package."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreatePackage, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'-t', '--template',
|
||||
metavar='<HEAT_TEMPLATE>',
|
||||
help=("Path to the Heat template to import as "
|
||||
"an Application Definition."),
|
||||
)
|
||||
parser.add_argument(
|
||||
'-c', '--classes-dir',
|
||||
metavar='<CLASSES_DIRECTORY>',
|
||||
help=("Path to the directory containing application classes."),
|
||||
)
|
||||
parser.add_argument(
|
||||
'-r', '--resources-dir',
|
||||
metavar='<RESOURCES_DIRECTORY>',
|
||||
help=("Path to the directory containing application resources."),
|
||||
)
|
||||
parser.add_argument(
|
||||
'-n', '--name',
|
||||
metavar='<DISPLAY_NAME>',
|
||||
help=("Display name of the Application in Catalog."),
|
||||
)
|
||||
parser.add_argument(
|
||||
'-f', '--full-name',
|
||||
metavar='<full-name>',
|
||||
help=("Fully-qualified name of the Application in Catalog."),
|
||||
)
|
||||
parser.add_argument(
|
||||
'-a', '--author',
|
||||
metavar='<AUTHOR>',
|
||||
help=("Name of the publisher."),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--tags',
|
||||
metavar='<TAG1 TAG2>',
|
||||
nargs='*',
|
||||
help=("A list of keywords connected to the application."),
|
||||
)
|
||||
parser.add_argument(
|
||||
'-d', '--description',
|
||||
metavar='<DESCRIPTION>',
|
||||
help=("Detailed description for the Application in Catalog."),
|
||||
)
|
||||
parser.add_argument(
|
||||
'-o', '--output',
|
||||
metavar='<PACKAGE_NAME>',
|
||||
help=("The name of the output file archive to save locally."),
|
||||
)
|
||||
parser.add_argument(
|
||||
'-u', '--ui',
|
||||
metavar='<UI_DEFINITION>',
|
||||
help=("Dynamic UI form definition."),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--type',
|
||||
metavar='<TYPE>',
|
||||
help=("Package type. Possible values: Application or Library."),
|
||||
)
|
||||
parser.add_argument(
|
||||
'-l', '--logo',
|
||||
metavar='<LOGO>',
|
||||
help=("Path to the package logo."),
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
LOG.debug("take_action({0})".format(parsed_args))
|
||||
parsed_args.os_username = os.getenv('OS_USERNAME')
|
||||
|
||||
def _make_archive(archive_name, path):
|
||||
zip_file = zipfile.ZipFile(archive_name, 'w')
|
||||
for root, dirs, files in os.walk(path):
|
||||
for f in files:
|
||||
zip_file.write(os.path.join(root, f),
|
||||
arcname=os.path.join(
|
||||
os.path.relpath(root, path), f))
|
||||
|
||||
if parsed_args.template and parsed_args.classes_dir:
|
||||
raise exc.CommandError(
|
||||
"Provide --template for a HOT-based package, OR"
|
||||
" --classes-dir for a MuranoPL-based package")
|
||||
if not parsed_args.template and not parsed_args.classes_dir:
|
||||
raise exc.CommandError(
|
||||
"Provide --template for a HOT-based package, OR at least"
|
||||
" --classes-dir for a MuranoPL-based package")
|
||||
directory_path = None
|
||||
try:
|
||||
archive_name = parsed_args.output if parsed_args.output else None
|
||||
if parsed_args.template:
|
||||
directory_path = hot_package.prepare_package(parsed_args)
|
||||
if not archive_name:
|
||||
archive_name = os.path.basename(parsed_args.template)
|
||||
archive_name = os.path.splitext(archive_name)[0] + ".zip"
|
||||
else:
|
||||
directory_path = mpl_package.prepare_package(parsed_args)
|
||||
if not archive_name:
|
||||
archive_name = tempfile.mkstemp(
|
||||
prefix="murano_", dir=os.getcwd())[1] + ".zip"
|
||||
|
||||
_make_archive(archive_name, directory_path)
|
||||
print("Application package is available at " +
|
||||
os.path.abspath(archive_name))
|
||||
finally:
|
||||
if directory_path:
|
||||
shutil.rmtree(directory_path)
|
@ -0,0 +1 @@
|
||||
heat_template_version: 2013-05-23
|
BIN
muranoclient/tests/unit/osc/v1/fixture_data/logo.png
Normal file
BIN
muranoclient/tests/unit/osc/v1/fixture_data/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,35 @@
|
||||
Namespaces:
|
||||
=: io.murano.apps.test
|
||||
std: io.murano
|
||||
res: io.murano.resources
|
||||
|
||||
Name: APP
|
||||
|
||||
Extends: std:Application
|
||||
|
||||
Properties:
|
||||
name:
|
||||
Contract: $.string().notNull()
|
||||
|
||||
instance:
|
||||
Contract: $.class(res:Instance).notNull()
|
||||
|
||||
|
||||
Workflow:
|
||||
initialize:
|
||||
Body:
|
||||
- $.environment: $.find(std:Environment).require()
|
||||
|
||||
deploy:
|
||||
Body:
|
||||
- $securityGroupIngress:
|
||||
- ToPort: 23
|
||||
FromPort: 23
|
||||
IpProtocol: tcp
|
||||
External: True
|
||||
- $.environment.securityGroupManager.addGroupIngress($securityGroupIngress)
|
||||
- $.instance.deploy()
|
||||
- $resources: new('io.murano.system.Resources')
|
||||
- $template: $resources.yaml('Deploy.template')
|
||||
- $.instance.agent.call($template, $resources)
|
||||
|
@ -0,0 +1,21 @@
|
||||
FormatVersion: 2.0.0
|
||||
Version: 1.0.0
|
||||
Name: Deploy
|
||||
|
||||
Parameters:
|
||||
appName: $appName
|
||||
|
||||
Body: |
|
||||
return deploy(args.appName).stdout
|
||||
|
||||
Scripts:
|
||||
deploy:
|
||||
Type: Application
|
||||
Version: 1.0.0
|
||||
EntryPoint: deploy.sh
|
||||
Files:
|
||||
- installer.sh
|
||||
- common.sh
|
||||
Options:
|
||||
captureStdout: true
|
||||
captureStderr: false
|
@ -0,0 +1 @@
|
||||
#!/bin/bash
|
@ -0,0 +1 @@
|
||||
#!/bin/bash
|
@ -0,0 +1 @@
|
||||
#!/bin/bash
|
@ -0,0 +1 @@
|
||||
Version: 2
|
103
muranoclient/tests/unit/osc/v1/test_package.py
Normal file
103
muranoclient/tests/unit/osc/v1/test_package.py
Normal file
@ -0,0 +1,103 @@
|
||||
# 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
|
||||
import six
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from testtools import matchers
|
||||
|
||||
from muranoclient.osc.v1 import package as osc_pkg
|
||||
from muranoclient.tests.unit.osc.v1 import fakes
|
||||
|
||||
from osc_lib import exceptions as exc
|
||||
|
||||
FIXTURE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
'fixture_data'))
|
||||
|
||||
|
||||
class TestPackage(fakes.TestApplicationCatalog):
|
||||
def setUp(self):
|
||||
super(TestPackage, self).setUp()
|
||||
self.package_mock = self.app.client_manager.application_catalog.\
|
||||
packages
|
||||
self.package_mock.reset_mock()
|
||||
|
||||
|
||||
class TestCreatePackage(TestPackage):
|
||||
def setUp(self):
|
||||
super(TestCreatePackage, self).setUp()
|
||||
|
||||
# Command to test
|
||||
self.cmd = osc_pkg.CreatePackage(self.app, None)
|
||||
|
||||
def test_create_package_without_args(self):
|
||||
arglist = []
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = self.assertRaises(exc.CommandError,
|
||||
self.cmd.take_action, parsed_args)
|
||||
self.assertEqual('Provide --template for a HOT-based package, OR at '
|
||||
'least --classes-dir for a MuranoPL-based package',
|
||||
str(error))
|
||||
|
||||
def test_create_package_template_and_classes_args(self):
|
||||
heat_template = os.path.join(FIXTURE_DIR, 'heat-template.yaml')
|
||||
classes_dir = os.path.join(FIXTURE_DIR, 'test-app', 'Classes')
|
||||
arglist = ['--template', heat_template, '--classes-dir', classes_dir]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = self.assertRaises(exc.CommandError,
|
||||
self.cmd.take_action, parsed_args)
|
||||
self.assertEqual('Provide --template for a HOT-based package, OR'
|
||||
' --classes-dir for a MuranoPL-based package',
|
||||
str(error))
|
||||
|
||||
def test_create_hot_based_package(self):
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
RESULT_PACKAGE = f.name
|
||||
heat_template = os.path.join(FIXTURE_DIR, 'heat-template.yaml')
|
||||
logo = os.path.join(FIXTURE_DIR, 'logo.png')
|
||||
arglist = ['--template', heat_template, '--output', RESULT_PACKAGE,
|
||||
'-l', logo]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
orig = sys.stdout
|
||||
try:
|
||||
sys.stdout = six.StringIO()
|
||||
self.cmd.take_action(parsed_args)
|
||||
finally:
|
||||
stdout = sys.stdout.getvalue()
|
||||
sys.stdout.close()
|
||||
sys.stdout = orig
|
||||
matchers.MatchesRegex(stdout,
|
||||
"Application package "
|
||||
"is available at {0}".format(RESULT_PACKAGE))
|
||||
|
||||
def test_create_mpl_package(self):
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
RESULT_PACKAGE = f.name
|
||||
classes_dir = os.path.join(FIXTURE_DIR, 'test-app', 'Classes')
|
||||
resources_dir = os.path.join(FIXTURE_DIR, 'test-app', 'Resources')
|
||||
ui = os.path.join(FIXTURE_DIR, 'test-app', 'ui.yaml')
|
||||
arglist = ['-c', classes_dir, '-r', resources_dir,
|
||||
'-u', ui, '-o', RESULT_PACKAGE]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
orig = sys.stdout
|
||||
try:
|
||||
sys.stdout = six.StringIO()
|
||||
self.cmd.take_action(parsed_args)
|
||||
finally:
|
||||
stdout = sys.stdout.getvalue()
|
||||
sys.stdout.close()
|
||||
sys.stdout = orig
|
||||
matchers.MatchesRegex(stdout,
|
||||
"Application package "
|
||||
"is available at {0}".format(RESULT_PACKAGE))
|
@ -51,6 +51,8 @@ openstack.application_catalog.v1 =
|
||||
|
||||
deployment_list = muranoclient.osc.v1.deployment:ListDeployment
|
||||
|
||||
package_create = muranoclient.osc.v1.package:CreatePackage
|
||||
|
||||
static-action_call = muranoclient.osc.v1.action:StaticActionCall
|
||||
class-schema = muranoclient.osc.v1.schema:ShowSchema
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user