CLI for Shipyard API

-Added cli folder
    -Utilizes click to process commands
	-commands can be found at
          https://github.com/att-comdev/shipyard/blob/master/docs/CLI.md
	-also, running the command 'shipyard'
         will give you a brief description of
         the commands available
    -Contains actions.py that calls Client API

-Added tests folder
    -Unit tests for commands
    -Negative unit tests are also included for
     commands
    -Unit tests for actions

Change-Id: Ie3e4d1f18aa935a311f332fba5c8a4d239183469
This commit is contained in:
One-Fine-Day 2017-10-03 15:36:44 -05:00
parent 2b4866f6cb
commit d679696ec0
47 changed files with 2456 additions and 12 deletions

View File

@ -31,3 +31,6 @@ requests==2.18.4
SQLAlchemy==1.1.13
ulid==1.1
uwsgi==2.0.15
click==6.7
click-default-group==1.2
pyyaml==3.12

View File

@ -17,12 +17,15 @@ classifier =
[files]
packages =
shipyard_airflow
shipyard_client
[entry_points]
oslo.config.opts =
shipyard_airflow = shipyard_airflow.conf.opts:list_opts
oslo.policy.policies =
shipyard_airflow = shipyard_airflow.policy:list_policies
console_scripts =
shipyard = shipyard_client.cli.commands:shipyard
[build_sphinx]
source-dir = docs/source

View File

@ -116,9 +116,8 @@ class ShipyardClient(BaseClient):
"""
action_data = {"name": name, "parameters": parameters}
url = ApiPaths.POST_GET_ACTIONS.value.format(self.shipyard_url)
return self.post_resp(url,
data=json.dumps(action_data),
content_type='application/json')
return self.post_resp(
url, data=json.dumps(action_data), content_type='application/json')
def get_action_detail(self, action_id=None):
"""

View File

@ -32,6 +32,7 @@ class ShipyardClientContext:
:param dict keystone_auth: auth_url, password, project_domain_name,
project_name, username, user_domain_name
"""
self.debug = debug
self.keystone_auth = keystone_auth
# the service type will for now just be shipyard will change later

View File

View File

@ -0,0 +1,72 @@
# Copyright 2017 AT&T Intellectual Property. All other 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.
# Base classes for cli actions intended to invoke the api
import logging
from shipyard_client.api_client.shipyard_api_client import ShipyardClient
from shipyard_client.api_client.shipyardclient_context import \
ShipyardClientContext
from shipyard_client.api_client.client_error import ClientError
from shipyard_client.cli.input_checks import validate_auth_vars
class CliAction(object):
"""Action base for CliActions"""
def __init__(self, ctx):
"""Sets api_client"""
self.logger = logging.getLogger('shipyard_cli')
self.api_parameters = ctx.obj['API_PARAMETERS']
self.resp_txt = ""
self.needs_credentials = False
auth_vars = self.api_parameters['auth_vars']
context_marker = self.api_parameters['context_marker']
debug = self.api_parameters['debug']
validate_auth_vars(self, ctx)
self.logger.debug("Passing environment varibles to the API client")
try:
shipyard_client_context = ShipyardClientContext(
auth_vars, context_marker, debug)
self.api_client = ShipyardClient(shipyard_client_context)
except ClientError as e:
self.logger.debug("The shipyard Client Context could not be set.")
ctx.fail('Client Error: %s.' % str(e))
def invoke_and_return_resp(self):
"""
calls the invoke method in the approiate actions.py and returns the
formatted response
"""
self.logger.debug("Inoking action.")
env_vars = self.api_parameters['auth_vars']
try:
self.invoke()
except ClientError as e:
self.resp_txt = "Client Error: %s." % str(e)
except Exception as e:
self.resp_txt = "Error: Unable to invoke action because %s." % str(
e)
return self.resp_txt
def invoke(self):
"""Default invoke"""
self.resp_txt = "Error: Invoke method is not defined for this action."

View File

@ -0,0 +1,137 @@
# Copyright 2017 AT&T Intellectual Property. All other 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-1.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 click
import logging
from click_default_group import DefaultGroup
from .commit import commands as commit
from .control import commands as control
from .create import commands as create
from .describe import commands as describe
from .get import commands as get
from .help import commands as help
from shipyard_client.cli.input_checks import check_control_action, check_id
@click.group(cls=DefaultGroup, default='shipyard_default')
# Shipyard Command Options
@click.option(
'--context-marker',
'context_marker',
help='Specifies a UUID (8-4-4-4-12 format) that will be used to correlate'
' logs, transactions, etc. in downstream activities triggered by this'
' interaction. If not specified, Shipyard will supply a new UUID to serve'
' as this marker. (optional)',
required=False)
@click.option('--debug/--no-debug', default=False, help='Turn Debug on.')
@click.option(
'--output-format',
'output_format',
required=False,
type=click.Choice(['format', 'raw']))
# Supported Environment Variables
@click.option(
'--os_project_domain_name',
envvar='OS_PROJECT_DOMAIN_NAME',
required=False)
@click.option(
'--os_user_domain_name', envvar='OS_USER_DOMAIN_NAME', required=False)
@click.option('--os_project_name', envvar='OS_PROJECT_NAME', required=False)
@click.option('--os_username', envvar='OS_USERNAME', required=False)
@click.option('--os_password', envvar='OS_PASSWORD', required=False)
# os_auth_url is required for all command except help, please see shipyard def
@click.option(
'--os_auth_url', envvar='OS_AUTH_URL', required=False)
# Allows context (ctx) to be passed
@click.pass_context
def shipyard(ctx, context_marker, debug, os_project_domain_name,
os_user_domain_name, os_project_name, os_username, os_password,
os_auth_url, output_format):
"""
COMMAND: shipyard \n
DESCRIPTION: The base shipyard command supports options that determine
cross-CLI behaviors. These options are positioned immediately following
the shipyard command. \n
FORMAT: shipyard [--context-marker=<uuid>] [--os_{various}=<value>]
[--debug/--no-debug] [--output-format=<json,yaml,raw] <subcommands> \n
"""
if not ctx.obj:
ctx.obj = {}
ctx.obj['DEBUG'] = debug
# setup logging for the CLI
# set up root logger
logger = logging.getLogger('shipyard_cli')
logger.setLevel(logging.DEBUG if debug else logging.INFO)
logging_handler = logging.StreamHandler()
logger.addHandler(logging_handler)
logger.debug('logging for cli initialized')
auth_vars = {
'project_domain_name': os_project_domain_name,
'user_domain_name': os_user_domain_name,
'project_name': os_project_name,
'username': os_username,
'password': os_password,
'auth_url': os_auth_url
}
ctx.obj['API_PARAMETERS'] = {
'auth_vars': auth_vars,
'context_marker': context_marker,
'debug': debug
}
ctx.obj['FORMAT'] = output_format
shipyard.add_command(commit.commit)
shipyard.add_command(control.control)
shipyard.add_command(create.create)
shipyard.add_command(describe.describe)
shipyard.add_command(get.get)
shipyard.add_command(help.help)
# To Invoke Control Commands
# Since control is not a command used in the CLI, the control commands
# pause, unpause, and stop are invoked here.
@shipyard.command(name='shipyard_default', short_help="")
@click.pass_context
@click.argument('action')
@click.argument('target_type', metavar='[TYPE]')
@click.argument('id', required=False)
def control_command(ctx, action, target_type, id):
"""
For more information on the control commands (pause, unpause, stop), please
enter 'shipyard control <control command> --help.'
"""
check_control_action(ctx, action)
if id:
check_id(ctx, id)
ctx.invoke(
getattr(control, 'control_' + action),
target_type=target_type,
id=id)
else:
ctx.invoke(
getattr(control, 'control_' + action), target_type=target_type)

View File

View File

@ -0,0 +1,35 @@
# Copyright 2017 AT&T Intellectual Property. All other 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 shipyard_client.cli.action import CliAction
from shipyard_client.cli.output_formatting import output_formatting
class CommitConfigdocs(CliAction):
"""Actions to Commit Configdocs"""
def __init__(self, ctx, force):
"""Initializes api_client, sets parameters, and sets output_format"""
super().__init__(ctx)
self.force = force
self.output_format = ctx.obj['FORMAT']
self.logger.debug("CommitConfigdocs action initialized with force=%s",
force)
def invoke(self):
"""Calls API Client and formats response from API Client"""
self.logger.debug("Calling API Client commit_configdocs.")
self.resp_txt = output_formatting(
self.output_format,
self.api_client.commit_configdocs(force=self.force))

View File

@ -0,0 +1,54 @@
# Copyright 2017 AT&T Intellectual Property. All other 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.
# Commit command
import click
from shipyard_client.cli.commit.actions import CommitConfigdocs
@click.group()
@click.pass_context
def commit(ctx):
"""
Commits confidocs. \n
For more information on commit commands
please enter the commit command followed by '--help' \n
Example: shipyard commit configdocs --help
"""
DESC_CONFIGDOCS = """
COMMAND: configdocs \n
DESCRIPTION: Attempts to commit the Shipyard Buffer documents, first
invoking validation by downstream components. \n
FORMAT: shipyard commit configdocs [--force] \n
EXAMPLE: shipyard commit configdocs
"""
SHORT_DESC_CONFIGDOCS = ("Attempts to commit the Shipyard Buffer documents, "
"first invoking validation by downstream components.")
@commit.command(
name='configdocs', help=DESC_CONFIGDOCS, short_help=SHORT_DESC_CONFIGDOCS)
@click.option(
'--force',
'-f',
flag_value=True,
help='Force the commit to occur, even if validations fail.')
@click.pass_context
def commit_configdocs(ctx, force):
click.echo(CommitConfigdocs(ctx, force).invoke_and_return_resp())

View File

View File

@ -0,0 +1,34 @@
# 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 shipyard_client.cli.action import CliAction
from shipyard_client.cli.output_formatting import output_formatting
class Control(CliAction):
"""Action to Pause Process"""
def __init__(self, ctx, control_verb, action_id):
"""Initializes api_client, sets parameters, and sets output_format"""
super().__init__(ctx)
self.action_id = action_id
self.control_verb = control_verb
self.output_format = ctx.obj['FORMAT']
self.logger.debug("ControlPause action initialized")
def invoke(self):
"""Calls API Client and formats response from API Client"""
self.logger.debug("Calling API Client post_control_action.")
self.resp_txt = output_formatting(self.output_format,
self.api_client.post_control_action(
action_id=self.action_id,
control_verb=self.control_verb))

View File

@ -0,0 +1,110 @@
# Copyright 2017 AT&T Intellectual Property. All other 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.
# Control command
import click
from shipyard_client.cli.control.actions import Control
@click.group()
@click.pass_context
def control(ctx):
"""
Pause, unpause, or stop something in progress. \n
For more information on control commands
please enter the control command followed by '--help' \n
Example: shipyard control pause --help
"""
DESC_PAUSE = """
COMMAND: pause \n
DESCRIPTION: Pause something in progress. \n
FORMAT: shipyard pause [<type> <id>] | [<qualified name>] \n
EXAMPLE: shipyard pause action 01BTG32JW87G0YKA1K29TKNAFX | shipyard pause
action/01BTG32JW87G0YKA1K29TKNAFX
"""
SHORT_DESC_PAUSE = "Pause something in progress."
@control.command(name='pause', help=DESC_PAUSE, short_help=SHORT_DESC_PAUSE)
@click.argument(
'target_type', metavar='[TYPE]', required=True) # type or qualified name
@click.argument('id', required=False)
@click.pass_context
def control_pause(ctx, target_type, id=None):
control_handler(ctx, target_type, 'pause', id)
DESC_UNPAUSE = """
COMMAND: unpause \n
DESCRIPTION: Unpause something in progress. \n
FORMAT: shipyard unpause [<type> <id>] | [<qualified name>] \n
EXAMPLE: shipyard unpause action 01BTG32JW87G0YKA1K29TKNAFX | shipyard
unpause action/01BTG32JW87G0YKA1K29TKNAFX
"""
SHORT_DESC_UNPAUSE = "Unpause something in progress."
@control.command(
name='unpause', help=DESC_UNPAUSE, short_help=SHORT_DESC_UNPAUSE)
@click.argument(
'target_type', metavar='[TYPE]', required=True) # type or qualified name
@click.argument('id', required=False)
@click.pass_context
def control_unpause(ctx, target_type, id=None):
control_handler(ctx, target_type, 'unpause', id)
DESC_STOP = """
COMMAND: stop \n
DESCRIPTION: Stop an executing or paused item. \n
FORMAT: shipyard stop [<type> <id>] | [<qualified name>] \n
EXAMPLE: shipyard stop action 01BTG32JW87G0YKA1K29TKNAFX | shipyard stop
action/01BTG32JW87G0YKA1K29TKNAFX
"""
SHORT_DESC_STOP = "Stop an executing or paused item."
@control.command(name='stop', help=DESC_STOP, short_help=SHORT_DESC_STOP)
@click.argument(
'target_type', metavar='[TYPE]', required=True) # type or qualified name
@click.argument('id', required=False)
@click.pass_context
def control_stop(ctx, target_type, id=None):
control_handler(ctx, target_type, 'stop', id)
def control_handler(ctx, target_type, control_verb, id):
"""
Handles spliting the qualified_name if needed and passes to actions.py
"""
# if id is not entered, then qualified name is entered
if id is None:
name = target_type.split("/")
if len(name) == 2:
target_type, id = name
else:
ctx.fail('A type and id must be entered.')
click.echo(Control(ctx, control_verb, id).invoke_and_return_resp())

View File

View File

@ -0,0 +1,59 @@
# 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 shipyard_client.cli.action import CliAction
from shipyard_client.cli.output_formatting import output_formatting
class CreateAction(CliAction):
"""Action to Create Action"""
def __init__(self, ctx, action_name, param):
"""Initializes api_client, sets parameters, and sets output_format"""
super().__init__(ctx)
self.logger.debug("CreateAction action initialized with action command"
"%s and parameters %s", action_name, param)
self.action_name = action_name
self.param = param
self.output_format = ctx.obj['FORMAT']
def invoke(self):
"""Calls API Client and formats response from API Client"""
self.logger.debug("Calling API Client post_actions.")
self.resp_txt = output_formatting(self.output_format,
self.api_client.post_actions(
name=self.action_name,
parameters=self.param))
class CreateConfigdocs(CliAction):
"""Action to Create Configdocs"""
def __init__(self, ctx, collection, buffer, data):
"""Initializes api_client, sets parameters, and sets output_format"""
super().__init__(ctx)
self.logger.debug("CreateConfigdocs action initialized with" +
" collection=%s,buffer=%s and data=%s", collection,
buffer, str(data))
self.collection = collection
self.buffer = buffer
self.data = data
self.output_format = ctx.obj['FORMAT']
def invoke(self):
"""Calls API Client and formats response from API Client"""
self.logger.debug("Calling API Client post_configdocs.")
self.resp_txt = output_formatting(self.output_format,
self.api_client.post_configdocs(
collection_id=self.collection,
buffer_mode=self.buffer,
document_data=self.data))

View File

@ -0,0 +1,147 @@
# Copyright 2017 AT&T Intellectual Property. All other 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.
# Create command
import click
import os
import yaml
from shipyard_client.cli.create.actions import CreateAction, CreateConfigdocs
from shipyard_client.cli.input_checks import check_action_command, \
check_reformat_parameter
@click.group()
@click.pass_context
def create(ctx):
"""
Create configdocs or an action. \n
For more information on create commands
please enter the create command followed by '--help' \n
Example: shipyard create action --help
"""
DESC_ACTION = """
COMMAND: action \n
DESCRIPTION: Invokes the specified workflow through Shipyard. Returns the
id of the action invoked so that it can be queried subsequently. \n
FORMAT: shipyard create action <action command> --param=<parameter>
(repeatable) \n
EXAMPLE: shipyard create action redeploy_server --param="server-name=mcp"
"""
SHORT_DESC_ACTION = (
"Invokes the specified workflow through Shipyard. Returns "
"the id of the action invoked so that it can be queried "
"subsequently.")
@create.command(name='action', help=DESC_ACTION, short_help=SHORT_DESC_ACTION)
@click.argument('action_name')
@click.option(
'--param',
multiple=True,
help="A parameter to be provided to the action being invoked.(Repeatable)")
@click.pass_context
def create_action(ctx, action_name, param):
check_action_command(ctx, action_name)
if not param and action_name is 'redeploy_server':
ctx.fail('At least one parameter must be specified using '
'--param="<parameter>" with action redeploy_server')
else:
param = check_reformat_parameter(ctx, param)
click.echo(
CreateAction(ctx, action_name, param).invoke_and_return_resp())
DESC_CONFIGDOCS = """
COMMAND: configdocs \n
DESCRIPTION: Load documents into the Shipyard Buffer. \n
FORMAT: shipyard create configdocs <collection> [--append | --replace]
[--filename=<filename> (repeatable) | --directory=<directory] \n
EXAMPLE: shipyard create configdocs design --append
--filename=site_design.yaml
"""
SHORT_DESC_CONFIGDOCS = "Load documents into the Shipyard Buffer."
@create.command(
name='configdocs', help=DESC_CONFIGDOCS, short_help=SHORT_DESC_CONFIGDOCS)
@click.argument('collection')
@click.option(
'--append',
flag_value=True,
help='Add the collection to the Shipyard Buffer. ')
@click.option(
'--replace',
flag_value=True,
help='Clear the Shipyard Buffer and replace it with the specified '
'contents. ')
@click.option(
'--filename',
multiple=True,
type=click.Path(exists=True),
help='The file name to use as the contents of the collection. '
'(Repeatable). ')
@click.option(
'--directory',
type=click.Path(exists=True),
help='A directory containing documents that will be joined and loaded as '
'a collection.')
@click.pass_context
def create_configdocs(ctx, collection, filename, directory, append, replace):
if (append and replace):
ctx.fail('Either append or replace may be selected but not both')
if (not filename and not directory) or (filename and directory):
ctx.fail('Please specify one or more filenames using '
'--filename="<filename>" OR a directory using '
'--directory="<directory>"')
if append:
create_buffer = 'append'
elif replace:
create_buffer = 'replace'
else:
create_buffer = None
if directory:
filename += tuple(
[each for each in os.listdir(directory) if each.endswith('.yaml')])
if filename is None:
ctx.fail('The directory does not contain any YAML files. Please '
'enter one or more YAML files or a directory that '
'contains one or more YAML files.')
docs = []
for file in filename:
with open(file, 'r') as stream:
if file.endswith(".yaml"):
try:
docs += list(yaml.safe_load_all(stream))
except yaml.YAMLError as exc:
ctx.fail('YAML file {} is invalid because {}'
.format(file, exc))
else:
ctx.fail('The file {} is not a YAML file. Please enter '
'only YAML files.'.format(file))
data = yaml.safe_dump_all(docs)
click.echo(
CreateConfigdocs(ctx, collection, create_buffer, data)
.invoke_and_return_resp())

View File

View File

@ -0,0 +1,77 @@
# 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 shipyard_client.cli.action import CliAction
from shipyard_client.cli.output_formatting import output_formatting
class DescribeAction(CliAction):
"""Action to Describe Action"""
def __init__(self, ctx, action_id):
"""Initializes api_client, sets parameters, and sets output_format"""
super().__init__(ctx)
self.logger.debug(
"DescribeAction action initialized with action_id=%s", action_id)
self.action_id = action_id
self.output_format = ctx.obj['FORMAT']
def invoke(self):
"""Calls API Client and formats response from API Client"""
self.logger.debug("Calling API Client get_action_detail.")
self.resp_txt = output_formatting(
self.output_format,
self.api_client.get_action_detail(action_id=self.action_id))
class DescribeStep(CliAction):
"""Action to Describe Step"""
def __init__(self, ctx, action_id, step_id):
"""Initializes api_client, sets parameters, and sets output_format"""
super().__init__(ctx)
self.logger.debug(
"DescribeStep action initialized with action_id=%s and step_id=%s",
action_id, step_id)
self.action_id = action_id
self.step_id = step_id
self.output_format = ctx.obj['FORMAT']
def invoke(self):
"""Calls API Client and formats response from API Client"""
self.logger.debug("Calling API Client get_step_detail.")
self.resp_txt = output_formatting(self.output_format,
self.api_client.get_step_detail(
action_id=self.action_id,
step_id=self.step_id))
class DescribeValidation(CliAction):
"""Action to Describe Validation"""
def __init__(self, ctx, action_id, validation_id):
"""Initializes api_client, sets parameters, and sets output_format"""
super().__init__(ctx)
self.logger.debug(
'DescribeValidation action initialized with action_id=%s'
'and validation_id=%s', action_id, validation_id)
self.validation_id = validation_id
self.action_id = action_id
self.output_format = ctx.obj['FORMAT']
def invoke(self):
"""Calls API Client and formats response from API Client"""
self.logger.debug("Calling API Client get_validation_detail.")
self.resp_txt = output_formatting(
self.output_format,
self.api_client.get_validation_detail(
action_id=self.action_id, validation_id=self.validation_id))

View File

@ -0,0 +1,153 @@
# Copyright 2017 AT&T Intellectual Property. All other 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.
# Describe command
import click
from click_default_group import DefaultGroup
from shipyard_client.cli.describe.actions import DescribeAction
from shipyard_client.cli.describe.actions import DescribeStep
from shipyard_client.cli.describe.actions import DescribeValidation
from shipyard_client.cli.input_checks import check_id
@click.group(cls=DefaultGroup, default='describe_default_command')
@click.pass_context
def describe(ctx):
"""
Describe the action, step, or validation. \n
For more information on describe commands
please enter the describe command followed by '--help' \n
Example: shipyard describe action --help \n
FOR NAMESPACE ENTRIES: \n
COMMAND: (no sub command) \n
DESCRIPTION: Retrieves the detailed information about the supplied
namespaced item. \n
FORMAT: shipyard describe <namespace item> \n
EXAMPLE: shipyard describe action/01BTG32JW87G0YKA1K29TKNAFX | shipyard
describe step/01BTG32JW87G0YKA1K29TKNAFX/preflight | shipyard describe
validation/01BTG32JW87G0YKA1K29TKNAFX/01BTG3PKBS15KCKFZ56XXXBGF2
"""
@describe.command('describe_default_command', short_help="")
@click.argument('namespace_item')
@click.pass_context
def describe_default_command(ctx, namespace_item):
try:
namespace = namespace_item.split("/")
if (namespace[0] == 'action'):
ctx.invoke(describe_action, action_id=namespace[1])
elif (namespace[0] == 'step'):
ctx.invoke(
describe_step, step_id=namespace[2], action=namespace[1])
elif (namespace[0] == 'validation'):
ctx.invoke(
describe_validation,
validation_id=namespace[1],
action=namespace[2])
else:
raise
except:
ctx.fail("Invalid namespace item. Please utilize one of the following"
" formats for the namespace item. \n"
"action: action/action id \n"
"step: step/action id/step id \n"
"validation: validation/validation id/action id")
DESC_ACTION = """
COMMAND: describe action \n
DESCRIPTION: Retrieves the detailed information about the supplied action
id. \n
FORMAT: shipyard describe action <action id> \n
EXAMPLE: shipyard describe action 01BTG32JW87G0YKA1K29TKNAFX
"""
SHORT_DESC_ACTION = ("Retrieves the detailed information about the supplied"
"action id.")
@describe.command('action', help=DESC_ACTION, short_help=SHORT_DESC_ACTION)
@click.argument('action_id')
@click.pass_context
def describe_action(ctx, action_id):
if not action_id:
click.fail("An action id argument must be passed.")
check_id(ctx, action_id)
click.echo(DescribeAction(ctx, action_id).invoke_and_return_resp())
DESC_STEP = """
COMMAND: describe step \n
DESCRIPTION: Retrieves the step details associated with an action and step. \n
FORMAT: shipyard describe step <step id> --action=<action id> \n
EXAMPLE: shipyard describe step preflight
--action=01BTG32JW87G0YKA1K29TKNAFX 786d416c-bf70-4705-a9bd-6670a4d7bb60
"""
SHORT_DESC_STEP = ("Retrieves the step details associated with an action and "
"step.")
@describe.command('step', help=DESC_STEP, short_help=SHORT_DESC_STEP)
@click.argument('step_id')
@click.option(
'--action',
'-a',
help='The action id that provides the context for this step')
@click.pass_context
def describe_step(ctx, step_id, action):
check_id(ctx, action)
click.echo(DescribeStep(ctx, action, step_id).invoke_and_return_resp())
DESC_VALIDATION = """
COMMAND: describe validation \n
DESCRIPTION: Retrieves the validation details assocaited with an action and
validation id. \n
FORMAT: shipyard describe validation <validation id> --action=<action id>\n
EXAMPLE: shipyard describe validation 01BTG3PKBS15KCKFZ56XXXBGF2
--action=01BTG32JW87G0YKA1K29TKNAFX
"""
SHORT_DESC_VALIDATION = ("Retrieves the validation details assocaited with an "
"action and validation id.")
@describe.command(
'validation', help=DESC_VALIDATION, short_help=SHORT_DESC_VALIDATION)
@click.argument('validation_id')
@click.option(
'--action',
'-a',
help='The action id that provides the context for this validation')
@click.pass_context
def describe_validation(ctx, validation_id, action):
check_id(ctx, validation_id)
check_id(ctx, action)
click.echo(
DescribeValidation(ctx, validation_id, action)
.invoke_and_return_resp())

View File

View File

@ -0,0 +1,70 @@
# 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 shipyard_client.cli.action import CliAction
from shipyard_client.cli.output_formatting import output_formatting
class GetActions(CliAction):
"""Action to Get Actions"""
def __init__(self, ctx):
"""Initializes api_client, sets parameters, and sets output_format"""
super().__init__(ctx)
self.logger.debug("GetActions action initialized.")
self.output_format = ctx.obj['FORMAT']
def invoke(self):
"""Calls API Client and formats response from API Client"""
self.logger.debug("Calling API Client get_actions.")
self.resp_txt = output_formatting(self.output_format,
self.api_client.get_actions())
class GetConfigdocs(CliAction):
"""Action to Get Configdocs"""
def __init__(self, ctx, collection, version):
"""Initializes api_client, sets parameters, and sets output_format"""
super().__init__(ctx)
self.logger.debug(
"GetConfigdocs action initialized with collection=%s and "
"version=%s" % (collection, version))
self.collection = collection
self.version = version
self.output_format = ctx.obj['FORMAT']
def invoke(self):
"""Calls API Client and formats response from API Client"""
self.logger.debug("Calling API Client get_configdocs.")
self.resp_txt = output_formatting(self.output_format,
self.api_client.get_configdocs(
collection_id=self.collection,
version=self.version))
class GetRenderedConfigdocs(CliAction):
"""Action to Get Rendered Configdocs"""
def __init__(self, ctx, version):
"""Initializes api_client, sets parameters, and sets output_format"""
super().__init__(ctx)
self.logger.debug("GetRenderedConfigdocs action initialized")
self.version = version
self.output_format = ctx.obj['FORMAT']
def invoke(self):
"""Calls API Client and formats response from API Client"""
self.logger.debug("Calling API Client get_rendereddocs.")
self.resp_txt = output_formatting(
self.output_format,
self.api_client.get_rendereddocs(version=self.version))

View File

@ -0,0 +1,143 @@
# Copyright 2017 AT&T Intellectual Property. All other 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.
# Get command
import click
from shipyard_client.cli.get.actions import GetActions
from shipyard_client.cli.get.actions import GetConfigdocs
from shipyard_client.cli.get.actions import GetRenderedConfigdocs
@click.group()
@click.pass_context
def get(ctx):
"""
Get the actions, confidocs, or renderedconfigdocs. \n
For more information on get commands
please enter the get command followed by '--help' \n
Example: shipyard get actions --help
"""
DESC_ACTIONS = """
COMMAND: actions \n
DESCRIPTION: Lists the actions that have been invoked. \n
FORMAT: shipyard get actions \n
EXAMPLE: shipyard get actions
"""
SHORT_DESC_ACTIONS = "Lists the actions that have been invoked."
@get.command(name='actions', help=DESC_ACTIONS, short_help=SHORT_DESC_ACTIONS)
@click.pass_context
def get_actions(ctx):
click.echo(GetActions(ctx).invoke_and_return_resp())
DESC_CONFIGDOCS = """
COMMAND: configdocs \n
DESCRIPTION: Retrieve documents loaded into Shipyard, either committed or
from the Shipyard Buffer. \n
FORMAT: shipyard get configdocs <collection> [--committed | --buffer] \n
EXAMPLE: shipyard get configdocs design
"""
SHORT_DESC_CONFIGDOCS = ("Retrieve documents loaded into Shipyard, either "
"committed or from the Shipyard Buffer.")
@get.command(
name='configdocs', help=DESC_CONFIGDOCS, short_help=SHORT_DESC_CONFIGDOCS)
@click.argument('collection')
@click.option(
'--committed',
'-c',
flag_value='committed',
help='Retrieve the documents that have last been committed for this '
'collection')
@click.option(
'--buffer',
'-b',
flag_value='buffer',
help='Retrive the documents that have been loaded into Shipyard since the '
'prior commit. If no documents have been loaded into the buffer for this '
'collection, this will return an empty response (default)')
@click.pass_context
def get_configdocs(ctx, collection, buffer, committed):
if buffer and committed:
ctx.fail(
'You must choose whether to retrive the committed OR from the '
'Shipyard Buffer with --committed or --buffer. ')
if (not buffer and not committed) or buffer:
version = 'buffer'
if committed:
version = 'committed'
click.echo(
GetConfigdocs(ctx, collection, version).invoke_and_return_resp())
DESC_RENDEREDCONFIGDOCS = """
COMMAND: renderedconfigdocs \n
DESCRIPTION: Retrieve the rendered version of documents loaded into
Shipyard. Rendered documents are the "final" version of the documents after
applying Deckhand layering and substitution. \n
FORMAT: shipyard get renderedconfigdocs [--committed | --buffer] \n
EXAMPLE: shipyard get renderedconfigdocs
"""
SHORT_DESC_RENDEREDCONFIGDOCS = (
"Retrieve the rendered version of documents "
"loaded into Shipyard. Rendered documents are "
"the 'final' version of the documents after "
"applying Deckhand layering and substitution.")
@get.command(
name='renderedconfigdocs',
help=DESC_RENDEREDCONFIGDOCS,
short_help=SHORT_DESC_RENDEREDCONFIGDOCS)
@click.option(
'--committed',
'-c',
flag_value='committed',
help='Retrieve the documents that have last been committed.')
@click.option(
'--buffer',
'-b',
flag_value='buffer',
help='Retrieve the documents that have been loaded into Shipyard since the'
' prior commit. (default)')
@click.pass_context
def get_renderedconfigdocs(ctx, buffer, committed):
if buffer and committed:
ctx.fail(
'You must choose whether to retrive the committed documents OR the'
' docutments in the Shipyard Buffer with --committed or --buffer.')
if committed:
version = 'committed'
else:
version = 'buffer'
click.echo(GetRenderedConfigdocs(ctx, version).invoke_and_return_resp())

View File

View File

@ -0,0 +1,49 @@
# Copyright 2017 AT&T Intellectual Property. All other 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.
# help command
import click
from shipyard_client.cli.help.output import default, actions, configdocs
@click.group()
def help():
"""Shipyard Help Command"""
pass
@help.command(name='help')
@click.argument('topic', required=False)
@click.pass_context
def help(ctx, topic=None):
"""
Display detailed information on topics. \n
COMMAND: help \n
DESCRIPTION: Provides topical help for Shipyard. Note that --help will
provide more specific command help. \n
FORMAT: shipyard help <topic> \n
EXAMPLE: shipyard help configdocs \n
"""
if topic is None:
click.echo(default())
elif topic == 'actions':
click.echo(actions())
elif topic == 'configdocs':
click.echo(configdocs())
else:
ctx.fail("Invalid topic. Run command 'shipyard help' for a list of "
" available topics.")

View File

@ -0,0 +1,64 @@
# Copyright 2017 AT&T Intellectual Property. All other 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.
def default():
return '''THE SHIPYARD COMMAND
The base shipyard command supports options that determine cross-CLI behaviors.
FORMAT
shipyard [--context-marker=<uuid>] [--os_{various}=<value>]
[--debug/--no-debug] [--output-format] <subcommands>
Please Note: --os_auth_url is required for every command except shipyard help
<topic>.
TOPICS
For information of the following topics, run shipyard help <topic>
actions
configdocs'''
def actions():
return '''ACTIONS
Supported Actions
No supported actions at this time.
Actions Under Development
deploy_site: Triggers the initial deployment of a site using the latest
committed configuration documents.
update_site: Triggers the initial deployment of a site, using the latest
committed configuration documents.
Steps:
(same as deploy_site)
redeploy_server: Using parameters to indicate which server(s), triggers a
redeployment of server to the last known good design and
secrets.
'''
def configdocs():
return '''CONFIGDOCS
The Shipyard Buffer Documents
Supported Commands:
shipyard commit configdocs
shipyard create configdocs
shipyard get configdocs'''

View File

@ -0,0 +1,67 @@
# Copyright 2017 AT&T Intellectual Property. All other 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-1.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.
def check_action_command(ctx, action_command):
"""verifies the action command is valid"""
if (action_command != "deploy_site") and (
action_command != "update_site") and (action_command !=
"redeploy_server"):
ctx.fail('Invalid action command. The action commands available are '
'deploy_site, update_site, and redeploy_server.')
def check_control_action(ctx, action):
"""verifies the control action is valid"""
if (action != 'pause') and (action != 'unpause') and (action != 'stop'):
ctx.fail('Invalid action. Please enter pause, unpause, or stop.')
def check_id(ctx, id):
"""verifies the id is valid"""
if (len(id) != 26):
ctx.fail('Invalid ID. ID can only be 26 characters.')
if not id.isalnum():
ctx.fail('Invalid ID. ID can only contain letters and numbers.')
def validate_auth_vars(self, ctx):
""" Checks that the required authurization varible have been entered """
required_auth_vars = ['auth_url']
auth_vars = self.api_parameters['auth_vars']
for var in required_auth_vars:
if auth_vars[var] is None:
self.resp_txt += (
'Missing the required authorization variable: ' + var + '\n')
if self.resp_txt is not "":
self.resp_txt += ('\nMissing the following additional authorization '
'options: ')
for var in auth_vars:
if auth_vars[var] is None and var not in required_auth_vars:
self.resp_txt += '\n--os_' + var
ctx.fail(self.resp_txt)
def check_reformat_parameter(ctx, param):
param_dictionary = {}
try:
for p in param:
values = p.split('=')
param_dictionary[values[0]] = values[1]
except:
ctx.fail(
"Invalid parameter or parameter format for " + p +
". Please utilize the format: <parameter name>=<parameter value>")
return param_dictionary

View File

@ -0,0 +1,47 @@
# Copyright 2017 AT&T Intellectual Property. All other 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-1.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
import yaml
def output_formatting(output_format, response):
"""formats response from api_client"""
if output_format == 'raw':
return response.text
else: # assume formatted
return formatted(response)
def formatted(response):
"""Formats either json or yaml depending on call"""
call = response.headers['Content-Type']
if 'json' in call:
try:
input = response.json()
return (json.dumps(input, sort_keys=True, indent=4))
except ValueError:
return ("This is not json and could not be printed as such. \n " +
response.text)
else: # all others should be yaml
try:
return (yaml.dump_all(
yaml.safe_load_all(response.content),
width=79,
indent=4,
default_flow_style=False))
except ValueError:
return ("This is not yaml and could not be printed as such.\n " +
response.text)

View File

@ -116,8 +116,7 @@ def test_commit_configs(*args):
def test_get_actions(*args):
shipyard_client = get_api_client()
result = shipyard_client.get_actions()
assert result['url'] == '{}/actions'.format(
shipyard_client.shipyard_url)
assert result['url'] == '{}/actions'.format(shipyard_client.shipyard_url)
@mock.patch.object(BaseClient, '__init__', replace_base_constructor)
@ -129,8 +128,7 @@ def test_post_actions(*args):
parameters = {'hello': 'world'}
result = shipyard_client.post_actions(name, parameters)
data = json.loads(result['data'])
assert result['url'] == '{}/actions'.format(
shipyard_client.shipyard_url)
assert result['url'] == '{}/actions'.format(shipyard_client.shipyard_url)
assert data['name'] == name
assert data['parameters']['hello'] == 'world'
@ -154,9 +152,8 @@ def test_get_val_details(*args):
action_id = 'GoodAction'
validation_id = 'Validation'
result = shipyard_client.get_validation_detail(action_id, validation_id)
assert result[
'url'] == '{}/actions/{}/validationdetails/{}'.format(
shipyard_client.shipyard_url, action_id, validation_id)
assert result['url'] == '{}/actions/{}/validationdetails/{}'.format(
shipyard_client.shipyard_url, action_id, validation_id)
@mock.patch.object(BaseClient, '__init__', replace_base_constructor)
@ -190,8 +187,8 @@ def test_get_workflows(*args):
shipyard_client = get_api_client()
since_mode = 'TestSince'
result = shipyard_client.get_workflows(since_mode)
assert result['url'] == '{}/workflows'.format(
shipyard_client.shipyard_url, since_mode)
assert result['url'] == '{}/workflows'.format(shipyard_client.shipyard_url,
since_mode)
@mock.patch.object(BaseClient, '__init__', replace_base_constructor)

View File

@ -0,0 +1,66 @@
# Copyright 2017 AT&T Intellectual Property. All other 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 mock
from shipyard_client.cli.commit.actions import CommitConfigdocs
from shipyard_client.api_client.base_client import BaseClient
from shipyard_client.tests.unit.cli.replace_api_client import \
replace_base_constructor, replace_post_rep, replace_get_resp, \
replace_output_formatting
from shipyard_client.tests.unit.cli.utils import temporary_context
from shipyard_client.api_client.shipyardclient_context import \
ShipyardClientContext
auth_vars = {
'project_domain_name': 'projDomainTest',
'user_domain_name': 'userDomainTest',
'project_name': 'projectTest',
'username': 'usernameTest',
'password': 'passwordTest',
'auth_url': 'urlTest'
}
api_parameters = {
'auth_vars': auth_vars,
'context_marker': 'UUID',
'debug': False
}
class MockCTX():
pass
ctx = MockCTX()
ctx.obj = {}
ctx.obj['API_PARAMETERS'] = api_parameters
ctx.obj['FORMAT'] = 'format'
@mock.patch.object(BaseClient, '__init__', replace_base_constructor)
@mock.patch.object(BaseClient, 'post_resp', replace_post_rep)
@mock.patch.object(BaseClient, 'get_resp', replace_get_resp)
@mock.patch.object(ShipyardClientContext, '__init__', temporary_context)
@mock.patch(
'shipyard_client.cli.commit.actions.output_formatting',
side_effect=replace_output_formatting)
def test_CommitConfigdocs(*args):
response = CommitConfigdocs(ctx, True).invoke_and_return_resp()
# test correct function was called
url = response.get('url')
assert 'commitconfigdocs' in url
# test function was called with correct parameters
params = response.get('params')
assert params.get('force') is True

View File

@ -0,0 +1,44 @@
# Copyright 2017 AT&T Intellectual Property. All other 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 click.testing import CliRunner
from mock import patch, ANY
from shipyard_client.cli.commit.actions import CommitConfigdocs
from shipyard_client.cli.commands import shipyard
auth_vars = ('--os_project_domain_name=OS_PROJECT_DOMAIN_NAME_test '
'--os_user_domain_name=OS_USER_DOMAIN_NAME_test '
'--os_project_name=OS_PROJECT_NAME_test '
'--os_username=OS_USERNAME_test --os_password=OS_PASSWORD_test '
'--os_auth_url=OS_AUTH_URL_test')
def test_commit_configdocs(*args):
"""test commit_configdocs"""
runner = CliRunner()
with patch.object(CommitConfigdocs, '__init__') as mock_method:
results = runner.invoke(shipyard, [auth_vars, 'commit', 'configdocs'])
mock_method.assert_called_once_with(ANY, False)
def test_commit_configdocs_negative():
"""
negative unit test for commit configdocs command
verifies invalid argument results in error
"""
invalid_arg = 'invalid'
runner = CliRunner()
results = runner.invoke(shipyard,
[auth_vars, 'commit', 'configdocs', invalid_arg])
assert 'Error' in results.output

View File

@ -0,0 +1,69 @@
# Copyright 2017 AT&T Intellectual Property. All other 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 mock
from shipyard_client.cli.control.actions import Control
from shipyard_client.api_client.base_client import BaseClient
from shipyard_client.tests.unit.cli.replace_api_client import \
replace_base_constructor, replace_post_rep, replace_get_resp, \
replace_output_formatting
from shipyard_client.tests.unit.cli.utils import temporary_context
from shipyard_client.api_client.shipyardclient_context import \
ShipyardClientContext
auth_vars = {
'project_domain_name': 'projDomainTest',
'user_domain_name': 'userDomainTest',
'project_name': 'projectTest',
'username': 'usernameTest',
'password': 'passwordTest',
'auth_url': 'urlTest'
}
api_parameters = {
'auth_vars': auth_vars,
'context_marker': 'UUID',
'debug': False
}
class MockCTX():
pass
ctx = MockCTX()
ctx.obj = {}
ctx.obj['API_PARAMETERS'] = api_parameters
ctx.obj['FORMAT'] = 'format'
@mock.patch.object(BaseClient, '__init__', replace_base_constructor)
@mock.patch.object(BaseClient, 'post_resp', replace_post_rep)
@mock.patch.object(BaseClient, 'get_resp', replace_get_resp)
@mock.patch.object(ShipyardClientContext, '__init__', temporary_context)
@mock.patch(
'shipyard_client.cli.control.actions.output_formatting',
side_effect=replace_output_formatting)
def test_Control(*args):
control_verb = 'pause'
id = '01BTG32JW87G0YKA1K29TKNAFX'
response = Control(ctx, control_verb, id).invoke_and_return_resp()
# test correct function was called
url = response.get('url')
assert 'control' in url
# test function was called with correct parameters
assert control_verb in url
assert id in url

View File

@ -0,0 +1,123 @@
# Copyright 2017 AT&T Intellectual Property. All other 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 click.testing import CliRunner
from mock import patch, ANY
from shipyard_client.cli.control.actions import Control
from shipyard_client.cli.commands import shipyard
auth_vars = ('--os_project_domain_name=OS_PROJECT_DOMAIN_NAME_test '
'--os_user_domain_name=OS_USER_DOMAIN_NAME_test '
'--os_project_name=OS_PROJECT_NAME_test '
'--os_username=OS_USERNAME_test --os_password=OS_PASSWORD_test '
'--os_auth_url=OS_AUTH_URL_test')
def test_control_pause(*args):
"""check that control_pause works for both type & id, or qaulified name"""
target_type = 'action'
id = '01BTG32JW87G0YKA1K29TKNAFX'
runner = CliRunner()
with patch.object(Control, '__init__') as mock_method:
results = runner.invoke(shipyard,
[auth_vars, 'pause', target_type, id])
mock_method.assert_called_once_with(ANY, 'pause', id)
qualified_name = target_type + "/" + id
with patch.object(Control, '__init__') as mock_method:
runner.invoke(shipyard, [auth_vars, 'pause', qualified_name])
mock_method.assert_called_once_with(ANY, 'pause', id)
def test_control_pause_negative(*args):
"""
negative unit test for control pause command
verifies invalid id and qualified name results in error
"""
target_type = 'action'
id = 'invalid id'
runner = CliRunner()
results = runner.invoke(shipyard, [auth_vars, 'pause', target_type, id])
assert 'Error' in results.output
qualified_name = 'invalid qualified name'
results = runner.invoke(shipyard, [auth_vars, 'pause', qualified_name])
assert 'Error' in results.output
def test_control_unpause(*args):
"""
check that control_unpause works for both type & id, or qaulified name
"""
target_type = 'action'
id = '01BTG32JW87G0YKA1K29TKNAFX'
runner = CliRunner()
with patch.object(Control, '__init__') as mock_method:
runner.invoke(shipyard, [auth_vars, 'unpause', target_type, id])
mock_method.assert_called_once_with(ANY, 'unpause', id)
qualified_name = target_type + "/" + id
with patch.object(Control, '__init__') as mock_method:
runner.invoke(shipyard, [auth_vars, 'unpause', qualified_name])
mock_method.assert_called_once_with(ANY, 'unpause', id)
def test_control_unpause_negative(*args):
"""
negative unit test for control unpause command
verifies invalid id and qualified name results in error
"""
target_type = 'action'
id = 'invalid id'
runner = CliRunner()
results = runner.invoke(shipyard, [auth_vars, 'unpause', target_type, id])
assert 'Error' in results.output
qualified_name = 'invalid qualified name'
results = runner.invoke(shipyard, [auth_vars, 'unpause', qualified_name])
assert 'Error' in results.output
def test_control_stop(*args):
"""check that control_stop works for both type & id, or qaulified name"""
target_type = 'action'
id = '01BTG32JW87G0YKA1K29TKNAFX'
runner = CliRunner()
with patch.object(Control, '__init__') as mock_method:
runner.invoke(shipyard, [auth_vars, 'stop', target_type, id])
mock_method.assert_called_once_with(ANY, 'stop', id)
qualified_name = target_type + "/" + id
with patch.object(Control, '__init__') as mock_method:
runner.invoke(shipyard, [auth_vars, 'stop', qualified_name])
mock_method.assert_called_once_with(ANY, 'stop', id)
def test_control_stop_negative(*args):
"""
negative unit test for control stop command
verifies invalid id and qualified name results in error
"""
target_type = 'action'
id = 'invalid id'
runner = CliRunner()
results = runner.invoke(shipyard, [auth_vars, 'stop', target_type, id])
assert 'Error' in results.output
qualified_name = 'invalid qualified name'
results = runner.invoke(shipyard, [auth_vars, 'stop', qualified_name])
assert 'Error' in results.output

View File

@ -0,0 +1,4 @@
env_description:
color : red
candy : crunch
food : strawberry

View File

@ -0,0 +1,9 @@
summer:
sport : tennis
vacation : Florida
temperature : 90F
---
fall:
holiday : Thanksgiving
food : pumpkin
color : orange

View File

@ -0,0 +1,95 @@
# Copyright 2017 AT&T Intellectual Property. All other 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 mock
import yaml
from shipyard_client.cli.create.actions import CreateAction, CreateConfigdocs
from shipyard_client.api_client.base_client import BaseClient
from shipyard_client.tests.unit.cli.replace_api_client import \
replace_base_constructor, replace_post_rep, replace_get_resp, \
replace_output_formatting
from shipyard_client.tests.unit.cli.utils import temporary_context
from shipyard_client.api_client.shipyardclient_context import \
ShipyardClientContext
auth_vars = {
'project_domain_name': 'projDomainTest',
'user_domain_name': 'userDomainTest',
'project_name': 'projectTest',
'username': 'usernameTest',
'password': 'passwordTest',
'auth_url': 'urlTest'
}
api_parameters = {
'auth_vars': auth_vars,
'context_marker': 'UUID',
'debug': False
}
class MockCTX():
pass
ctx = MockCTX()
ctx.obj = {}
ctx.obj['API_PARAMETERS'] = api_parameters
ctx.obj['FORMAT'] = 'format'
@mock.patch.object(BaseClient, '__init__', replace_base_constructor)
@mock.patch.object(BaseClient, 'post_resp', replace_post_rep)
@mock.patch.object(BaseClient, 'get_resp', replace_get_resp)
@mock.patch.object(ShipyardClientContext, '__init__', temporary_context)
@mock.patch(
'shipyard_client.cli.create.actions.output_formatting',
side_effect=replace_output_formatting)
def test_CreateAction(*args):
action_name = 'redeploy_server'
param = {'server-name': 'mcp'}
response = CreateAction(ctx, action_name, param).invoke_and_return_resp()
# test correct function was called
url = response.get('url')
assert 'actions' in url
# test function was called with correct parameters
data = response.get('data')
assert '"name": "redeploy_server"' in data
assert '"parameters": {"server-name": "mcp"}' in data
@mock.patch.object(BaseClient, '__init__', replace_base_constructor)
@mock.patch.object(BaseClient, 'post_resp', replace_post_rep)
@mock.patch.object(BaseClient, 'get_resp', replace_get_resp)
@mock.patch.object(ShipyardClientContext, '__init__', temporary_context)
@mock.patch(
'shipyard_client.cli.create.actions.output_formatting',
side_effect=replace_output_formatting)
def test_CreateConfigdocs(*args):
collection = 'design'
filename = 'shipyard_client/tests/unit/cli/create/sample_yaml/sample.yaml'
document_data = yaml.dump_all(filename)
buffer = 'append'
response = CreateConfigdocs(ctx, collection, buffer,
document_data).invoke_and_return_resp()
# test correct function was called
url = response.get('url')
assert 'configdocs' in url
# test function was called with correct parameters
assert collection in url
data = response.get('data')
assert document_data in data
params = response.get('params')
assert params.get('buffermode') == buffer

View File

@ -0,0 +1,83 @@
# Copyright 2017 AT&T Intellectual Property. All other 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 click.testing import CliRunner
from mock import patch, ANY
from shipyard_client.cli.create.actions import CreateAction, CreateConfigdocs
from shipyard_client.cli.commands import shipyard
auth_vars = ('--os_project_domain_name=OS_PROJECT_DOMAIN_NAME_test '
'--os_user_domain_name=OS_USER_DOMAIN_NAME_test '
'--os_project_name=OS_PROJECT_NAME_test '
'--os_username=OS_USERNAME_test --os_password=OS_PASSWORD_test '
'--os_auth_url=OS_AUTH_URL_test')
def test_create_action():
"""test create_action works with action id and param input"""
action_name = 'redeploy_server'
param = '--param="server-name=mcp"'
runner = CliRunner()
with patch.object(CreateAction, '__init__') as mock_method:
runner.invoke(shipyard,
[auth_vars, 'create', 'action', action_name, param])
mock_method.assert_called_once_with(ANY, action_name,
{'"server-name': 'mcp"'})
def test_create_action_negative():
"""
negative unit test for create action command
verifies invalid action command results in error
"""
action_command = 'invalid_action_command'
param = '--param="test"'
runner = CliRunner()
results = runner.invoke(
shipyard, [auth_vars, 'create', 'action', action_command, param])
assert 'Error' in results.output
def test_create_configdocs():
"""test create configdocs"""
collection = 'design'
filename = 'shipyard_client/tests/unit/cli/create/sample_yaml/sample.yaml'
append = 'append'
runner = CliRunner()
with patch.object(CreateConfigdocs, '__init__') as mock_method:
runner.invoke(shipyard, [
auth_vars, 'create', 'configdocs', collection, '--' + append,
'--filename=' + filename
])
mock_method.assert_called_once_with(ANY, collection, 'append', ANY)
def test_create_configdocs_negative():
"""
negative unit test for create configdocs command
verifies invalid filename results in error
"""
collection = 'design'
filename = 'invalid.yaml'
append = 'append'
runner = CliRunner()
results = runner.invoke(shipyard, [
auth_vars, 'create', 'configdocs', collection, '--' + append,
'--filename=' + filename
])
assert 'Error' in results.output

View File

@ -0,0 +1,99 @@
# Copyright 2017 AT&T Intellectual Property. All other 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 mock
from shipyard_client.cli.describe.actions import \
DescribeAction, DescribeStep, DescribeValidation
from shipyard_client.api_client.base_client import BaseClient
from shipyard_client.tests.unit.cli.replace_api_client import \
replace_base_constructor, replace_post_rep, replace_get_resp, \
replace_output_formatting
from shipyard_client.tests.unit.cli.utils import temporary_context
from shipyard_client.api_client.shipyardclient_context import \
ShipyardClientContext
auth_vars = {
'project_domain_name': 'projDomainTest',
'user_domain_name': 'userDomainTest',
'project_name': 'projectTest',
'username': 'usernameTest',
'password': 'passwordTest',
'auth_url': 'urlTest'
}
api_parameters = {
'auth_vars': auth_vars,
'context_marker': 'UUID',
'debug': False
}
class MockCTX():
pass
ctx = MockCTX()
ctx.obj = {}
ctx.obj['API_PARAMETERS'] = api_parameters
ctx.obj['FORMAT'] = 'format'
@mock.patch.object(BaseClient, '__init__', replace_base_constructor)
@mock.patch.object(BaseClient, 'post_resp', replace_post_rep)
@mock.patch.object(BaseClient, 'get_resp', replace_get_resp)
@mock.patch.object(ShipyardClientContext, '__init__', temporary_context)
@mock.patch(
'shipyard_client.cli.describe.actions.output_formatting',
side_effect=replace_output_formatting)
def test_DescribeAction(*args):
response = DescribeAction(
ctx, '01BTG32JW87G0YKA1K29TKNAFX').invoke_and_return_resp()
# test correct function was called
url = response.get('url')
assert 'actions/01BTG32JW87G0YKA1K29TKNAFX' in url
@mock.patch.object(BaseClient, '__init__', replace_base_constructor)
@mock.patch.object(BaseClient, 'post_resp', replace_post_rep)
@mock.patch.object(BaseClient, 'get_resp', replace_get_resp)
@mock.patch.object(ShipyardClientContext, '__init__', temporary_context)
@mock.patch(
'shipyard_client.cli.describe.actions.output_formatting',
side_effect=replace_output_formatting)
def test_DescribeStep(*args):
response = DescribeStep(ctx, '01BTG32JW87G0YKA1K29TKNAFX',
'preflight').invoke_and_return_resp()
# test correct function was called
url = response.get('url')
assert 'actions/01BTG32JW87G0YKA1K29TKNAFX/steps/preflight' in url
@mock.patch.object(BaseClient, '__init__', replace_base_constructor)
@mock.patch.object(BaseClient, 'post_resp', replace_post_rep)
@mock.patch.object(BaseClient, 'get_resp', replace_get_resp)
@mock.patch.object(ShipyardClientContext, '__init__', temporary_context)
@mock.patch(
'shipyard_client.cli.describe.actions.output_formatting',
side_effect=replace_output_formatting)
def test_DescribeValidation(*args):
response = DescribeValidation(
ctx, '01BTG32JW87G0YKA1K29TKNAFX',
'01BTG3PKBS15KCKFZ56XXXBGF2').invoke_and_return_resp()
# test correct function was called
url = response.get('url')
assert 'actions' in url
assert '01BTG32JW87G0YKA1K29TKNAFX' in url
assert 'validationdetails' in url
assert '01BTG3PKBS15KCKFZ56XXXBGF2' in url

View File

@ -0,0 +1,117 @@
# Copyright 2017 AT&T Intellectual Property. All other 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 click.testing import CliRunner
from mock import patch, ANY
from shipyard_client.cli.describe.actions import DescribeAction
from shipyard_client.cli.describe.actions import DescribeStep
from shipyard_client.cli.describe.actions import DescribeValidation
from shipyard_client.cli.commands import shipyard
auth_vars = ('--os_project_domain_name=OS_PROJECT_DOMAIN_NAME_test '
'--os_user_domain_name=OS_USER_DOMAIN_NAME_test '
'--os_project_name=OS_PROJECT_NAME_test '
'--os_username=OS_USERNAME_test --os_password=OS_PASSWORD_test '
'--os_auth_url=OS_AUTH_URL_test')
def test_describe_action():
"""test describe_action"""
action_id = '01BTG32JW87G0YKA1K29TKNAFX'
runner = CliRunner()
with patch.object(DescribeAction, '__init__') as mock_method:
runner.invoke(shipyard, [auth_vars, 'describe', 'action', action_id])
mock_method.assert_called_once_with(ANY, action_id)
def test_describe_action_negative():
"""
negative unit test for describe action command
verifies invalid action id results in error
"""
action_id = 'invalid action id'
runner = CliRunner()
results = runner.invoke(shipyard,
[auth_vars, 'describe', 'action', action_id])
assert 'Error' in results.output
def test_describe_step():
"""test describe_step"""
step_id = 'preflight'
action_id = '01BTG32JW87G0YKA1K29TKNAFX'
runner = CliRunner()
with patch.object(DescribeStep, '__init__') as mock_method:
results = runner.invoke(shipyard, [
auth_vars, 'describe', 'step', step_id, '--action=' + action_id
])
mock_method.assert_called_once_with(ANY, action_id, step_id)
def test_describe_step_negative():
"""
negative unit test for describe step command
verifies invalid action id results in error
"""
step_id = 'preflight'
action_id = 'invalid action id'
runner = CliRunner()
results = runner.invoke(shipyard, [
auth_vars, 'describe', 'step', step_id, '--action=' + action_id
])
assert 'Error' in results.output
def test_describe_validation():
"""test describe_validation"""
validation_id = '01BTG3PKBS15KCKFZ56XXXBGF2'
action_id = '01BTG32JW87G0YKA1K29TKNAFX'
runner = CliRunner()
with patch.object(DescribeValidation, '__init__') as mock_method:
runner.invoke(shipyard, [
auth_vars, 'describe', 'validation', '01BTG3PKBS15KCKFZ56XXXBGF2',
'--action=' + action_id
])
mock_method.assert_called_once_with(ANY, validation_id, action_id)
def test_describe_validation_negative():
"""
negative unit test for describe validation command
verifies invalid validation id and action id results in error
"""
validation_id = 'invalid validation id'
action_id = '01BTG32JW87G0YKA1K29TKNAFX'
runner = CliRunner()
results = runner.invoke(shipyard, [
auth_vars, 'describe', 'validation', validation_id,
'--action=' + action_id
])
assert 'Error' in results.output
validation_id = '01BTG3PKBS15KCKFZ56XXXBGF2'
action_id = 'invalid action id'
runner = CliRunner()
results = runner.invoke(shipyard, [
auth_vars, 'describe', 'validation', validation_id,
'--action=' + action_id
])
assert 'Error' in results.output

View File

@ -0,0 +1,97 @@
# Copyright 2017 AT&T Intellectual Property. replace_shipyard All other 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 mock
from shipyard_client.cli.get.actions import GetActions, GetConfigdocs, \
GetRenderedConfigdocs
from shipyard_client.api_client.base_client import BaseClient
from shipyard_client.tests.unit.cli.replace_api_client import \
replace_base_constructor, replace_post_rep, replace_get_resp, \
replace_output_formatting
from shipyard_client.tests.unit.cli.utils import temporary_context
from shipyard_client.api_client.shipyardclient_context import \
ShipyardClientContext
auth_vars = {
'project_domain_name': 'projDomainTest',
'user_domain_name': 'userDomainTest',
'project_name': 'projectTest',
'username': 'usernameTest',
'password': 'passwordTest',
'auth_url': 'urlTest'
}
api_parameters = {
'auth_vars': auth_vars,
'context_marker': 'UUID',
'debug': False
}
class MockCTX():
pass
ctx = MockCTX()
ctx.obj = {}
ctx.obj['API_PARAMETERS'] = api_parameters
ctx.obj['FORMAT'] = 'format'
@mock.patch.object(BaseClient, '__init__', replace_base_constructor)
@mock.patch.object(BaseClient, 'post_resp', replace_post_rep)
@mock.patch.object(BaseClient, 'get_resp', replace_get_resp)
@mock.patch.object(ShipyardClientContext, '__init__', temporary_context)
@mock.patch(
'shipyard_client.cli.get.actions.output_formatting',
side_effect=replace_output_formatting)
def test_GetActions(*args):
response = GetActions(ctx).invoke_and_return_resp()
# test correct function was called
url = response.get('url')
assert 'actions' in url
assert response.get('params') == {}
@mock.patch.object(BaseClient, '__init__', replace_base_constructor)
@mock.patch.object(BaseClient, 'post_resp', replace_post_rep)
@mock.patch.object(BaseClient, 'get_resp', replace_get_resp)
@mock.patch.object(ShipyardClientContext, '__init__', temporary_context)
@mock.patch(
'shipyard_client.cli.get.actions.output_formatting',
side_effect=replace_output_formatting)
def test_GetConfigdocs(*args):
response = GetConfigdocs(ctx, 'design', 'buffer').invoke_and_return_resp()
# test correct function was called
url = response.get('url')
assert 'configdocs/design' in url
params = response.get('params')
assert params.get('version') == 'buffer'
@mock.patch.object(BaseClient, '__init__', replace_base_constructor)
@mock.patch.object(BaseClient, 'post_resp', replace_post_rep)
@mock.patch.object(BaseClient, 'get_resp', replace_get_resp)
@mock.patch.object(ShipyardClientContext, '__init__', temporary_context)
@mock.patch(
'shipyard_client.cli.get.actions.output_formatting',
side_effect=replace_output_formatting)
def test_GetRenderedConfigdocs(*args):
response = GetRenderedConfigdocs(ctx, 'buffer').invoke_and_return_resp()
# test correct function was called
url = response.get('url')
assert 'renderedconfigdocs' in url
params = response.get('params')
assert params.get('version') == 'buffer'

View File

@ -0,0 +1,94 @@
# Copyright 2017 AT&T Intellectual Property. All other 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 click.testing import CliRunner
from mock import patch, ANY
from shipyard_client.cli.get.actions import (GetActions, GetConfigdocs,
GetRenderedConfigdocs)
from shipyard_client.cli.commands import shipyard
auth_vars = ('--os_project_domain_name=OS_PROJECT_DOMAIN_NAME_test '
'--os_user_domain_name=OS_USER_DOMAIN_NAME_test '
'--os_project_name=OS_PROJECT_NAME_test '
'--os_username=OS_USERNAME_test --os_password=OS_PASSWORD_test '
'--os_auth_url=OS_AUTH_URL_test')
def test_get_actions(*args):
"""test get_actions"""
runner = CliRunner()
with patch.object(GetActions, '__init__') as mock_method:
runner.invoke(shipyard, [auth_vars, 'get', 'actions'])
mock_method.assert_called_once_with(ANY)
def test_get_actions_negative(*args):
"""
negative unit test for get actions command
verifies invalid argument results in error
"""
invalid_arg = 'invalid'
runner = CliRunner()
results = runner.invoke(shipyard,
[auth_vars, 'get', 'actions', invalid_arg])
assert 'Error' in results.output
def test_get_configdocs(*args):
"""test get_configdocs"""
collection = 'design'
runner = CliRunner()
with patch.object(GetConfigdocs, '__init__') as mock_method:
runner.invoke(shipyard, [auth_vars, 'get', 'configdocs', collection])
mock_method.assert_called_once_with(ANY, collection, 'buffer')
def test_get_configdocs_negative(*args):
"""
negative unit test for get actions command
verifies invalid argument results in error
"""
collection = 'design'
invalid_arg = 'invalid'
runner = CliRunner()
results = runner.invoke(
shipyard, [auth_vars, 'get', 'configdocs', collection, invalid_arg])
assert 'Error' in results.output
def test_get_renderedconfigdocs(*args):
"""test get_rendereddocs"""
runner = CliRunner()
with patch.object(GetRenderedConfigdocs, '__init__') as mock_method:
runner.invoke(shipyard, [auth_vars, 'get', 'renderedconfigdocs'])
mock_method.assert_called_once_with(ANY, 'buffer')
def test_get_renderedconfigdocs_negative(*args):
"""
negative unit test for get actions command
verfies invalid argument results in error
"""
invalid_arg = 'invalid'
runner = CliRunner()
results = runner.invoke(
shipyard, [auth_vars, 'get', 'renderedconfigdocs', invalid_arg])
assert 'Error' in results.output

View File

@ -0,0 +1,45 @@
# Copyright 2017 AT&T Intellectual Property. All other 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 click.testing import CliRunner
from shipyard_client.cli.help.commands import help
def test_help():
"""test help with all options"""
runner = CliRunner()
result = runner.invoke(help)
assert 'THE SHIPYARD COMMAND' in result.output
topic = 'actions'
result = runner.invoke(help, [topic])
assert 'ACTIONS' in result.output
topic = 'configdocs'
result = runner.invoke(help, [topic])
assert 'CONFIGDOCS' in result.output
def test_help_negative():
"""
negative unit test for help command
verfies invalid topic results in error
"""
invalid_topic = 'invalid'
runner = CliRunner()
result = runner.invoke(help, [invalid_topic])
assert 'Error' in result.output

View File

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
#
# 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.
# For testing purposes only
class TemporaryContext(object):
def __init__(self):
self.debug = True
self.keystone_Auth = {}
self.token = 'abcdefgh'
self.service_type = 'http://shipyard'
self.shipyard_endpoint = 'http://shipyard/api/v1.0'
self.context_marker = '123456'
def replace_post_rep(self, url, query_params={}, data={}, content_type=''):
"""
replaces call to shipyard client
:returns: dict with url and parameters
"""
return {'url': url, 'params': query_params, 'data': data}
def replace_get_resp(self, url, query_params={}, json=False):
"""
replaces call to shipyard client
:returns: dict with url and parameters
"""
return {'url': url, 'params': query_params}
def replace_base_constructor(self, context):
pass
def replace_output_formatting(format, response):
return response

View File

@ -0,0 +1,56 @@
# Copyright 2017 AT&T Intellectual Property. All other 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
import yaml
from mock import patch, ANY
from requests.models import Response
from shipyard_client.cli.output_formatting import output_formatting
json_response = Response()
json_response._content = b'{ "key" : "a" }'
json_response.status_code = 200
json_response.headers['content-type'] = 'application/json'
yaml_response = Response()
yaml_response._content = b'''Projects:
C/C++ Libraries:
- libyaml # "C" Fast YAML 1.1
- Syck # (dated) "C" YAML 1.0
- yaml-cpp # C++ YAML 1.2 implementation
Ruby:
- psych # libyaml wrapper (in Ruby core for 1.9.2)
- RbYaml # YAML 1.1 (PyYAML Port)
- yaml4r # YAML 1.0, standard library syck binding
Python:
- PyYAML # YAML 1.1, pure python and libyaml binding
- ruamel.yaml # YAML 1.2, update of PyYAML with round-tripping of comments
- PySyck # YAML 1.0, syck binding'''
yaml_response.headers['content-type'] = 'application/yaml'
def test_output_formatting():
"""call output formatting and check correct one was given"""
with patch.object(json, 'dumps') as mock_method:
output_formatting('format', json_response)
mock_method.assert_called_once_with(
json_response.json(), sort_keys=True, indent=4)
with patch.object(yaml, 'dump_all') as mock_method:
output_formatting('format', yaml_response)
mock_method.assert_called_once_with(
ANY, width=79, indent=4, default_flow_style=False)

View File

@ -0,0 +1,50 @@
# Copyright 2017 AT&T Intellectual Property. All other 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 click.testing import CliRunner
from mock import patch
from shipyard_client.cli.commands import shipyard
from shipyard_client.api_client.shipyardclient_context import \
ShipyardClientContext
def test_shipyard():
context_marker = '--context-marker=UUID'
debug = '--debug'
os_project_domain_name = (
'--os_project_domain_name=OS_PROJECT_DOMAIN_NAME_test')
os_user_domain_name = '--os_user_domain_name=OS_USER_DOMAIN_NAME_test'
os_project_name = '--os_project_name=OS_PROJECT_NAME_test'
os_username = '--os_username=OS_USERNAME_test'
os_password = '--os_password=OS_PASSWORD_test'
os_auth_url = '--os_auth_url=OS_AUTH_URL_test'
auth_vars = {
'project_domain_name': 'OS_PROJECT_DOMAIN_NAME_test',
'user_domain_name': 'OS_USER_DOMAIN_NAME_test',
'project_name': 'OS_PROJECT_NAME_test',
'username': 'OS_USERNAME_test',
'password': 'OS_PASSWORD_test',
'auth_url': 'OS_AUTH_URL_test'
}
runner = CliRunner()
with patch.object(ShipyardClientContext, '__init__') as mock_method:
results = runner.invoke(shipyard, [
context_marker, os_project_domain_name, os_user_domain_name,
os_project_name, os_username, os_password, os_auth_url, debug,
'commit', 'configdocs'
])
mock_method.assert_called_once_with(auth_vars, 'UUID', True)

View File

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
#
# 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.
# For testing purposes only
def temporary_context(self, keystone_auth, context_marker, debug=False):
self.debug = debug
self.keystone_Auth = keystone_auth
self.service_type = 'shipyard'
self.shipyard_endpoint = 'http://shipyard/api/v1.0'
self.context_marker = context_marker