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:
zhurong 2016-08-05 08:09:24 +00:00
parent f7bafa1b80
commit 85d360873e
11 changed files with 304 additions and 0 deletions

View 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)

View File

@ -0,0 +1 @@
heat_template_version: 2013-05-23

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -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)

View File

@ -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

View File

@ -0,0 +1 @@
Version: 2

View 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))

View File

@ -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