[envs] Implmenet Env CLI commands
- Introduce Env CLI commands & bash completition - Use RALLY_ENV variable in both cases for deployments and env - Smooth RALLY_DEPLOYMENT -> RALLY_ENV migration - Implement unit tests - Implement functional tests Change-Id: I56a60bd35bdc5ff833fdcad19f13ecf55496a316
This commit is contained in:
parent
eeb1de65f7
commit
e7d6155298
@ -32,6 +32,14 @@ _rally()
|
||||
OPTS["deployment_recreate"]="--filename --deployment"
|
||||
OPTS["deployment_show"]="--deployment"
|
||||
OPTS["deployment_use"]="--deployment"
|
||||
OPTS["env_check"]="--env --json --detailed"
|
||||
OPTS["env_create"]="--name --description --extras --spec --json --no-use"
|
||||
OPTS["env_delete"]="--env --force"
|
||||
OPTS["env_destroy"]="--env --skip-cleanup --json --detailed"
|
||||
OPTS["env_info"]="--env --json"
|
||||
OPTS["env_list"]="--json"
|
||||
OPTS["env_show"]="--env --json"
|
||||
OPTS["env_use"]="--env --json"
|
||||
OPTS["plugin_list"]="--name --platform --plugin-base"
|
||||
OPTS["plugin_show"]="--name --platform"
|
||||
OPTS["task_abort"]="--uuid --soft"
|
||||
@ -97,4 +105,4 @@ _rally()
|
||||
return 0
|
||||
}
|
||||
|
||||
complete -o filenames -F _rally rally
|
||||
complete -o filenames -F _rally rally
|
||||
|
@ -309,7 +309,9 @@ class DeploymentCommands(object):
|
||||
return 1
|
||||
print("Using deployment: %s" % deployment["uuid"])
|
||||
|
||||
fileutils.update_globals_file("RALLY_DEPLOYMENT",
|
||||
fileutils.update_globals_file(envutils.ENV_DEPLOYMENT,
|
||||
deployment["uuid"])
|
||||
fileutils.update_globals_file(envutils.ENV_ENV,
|
||||
deployment["uuid"])
|
||||
|
||||
if "openstack" in deployment["credentials"]:
|
||||
|
269
rally/cli/commands/env.py
Normal file
269
rally/cli/commands/env.py
Normal file
@ -0,0 +1,269 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import json
|
||||
import os
|
||||
import prettytable
|
||||
import traceback
|
||||
|
||||
from rally.cli import cliutils
|
||||
from rally.cli import envutils
|
||||
from rally.common import fileutils
|
||||
from rally.common import yamlutils as yaml
|
||||
from rally.env import env_mgr
|
||||
from rally import exceptions
|
||||
|
||||
|
||||
YES = u":-)"
|
||||
NO = u":-("
|
||||
|
||||
|
||||
def _print(msg, silent=False):
|
||||
if not silent:
|
||||
print(msg)
|
||||
|
||||
|
||||
# TODO(boris-42): Wrap all methods to catch EnvManager Exceptions
|
||||
class EnvCommands(object):
|
||||
"""Set of commands that allow you to manage envs."""
|
||||
|
||||
@cliutils.args("--name", "-n", type=str, required=True,
|
||||
help="Name of the env.")
|
||||
@cliutils.args("--description", "-d", type=str, required=False,
|
||||
help="Env description")
|
||||
@cliutils.args("--extras", "-e", type=str, required=False,
|
||||
help="JSON or YAML dict with custom non validate info.")
|
||||
@cliutils.args("--spec", "-s", type=str, required=False,
|
||||
metavar="<path>", help="Path to env spec.")
|
||||
@cliutils.args("--json", action="store_true", dest="to_json",
|
||||
help="Format output as JSON.")
|
||||
@cliutils.args("--no-use", action="store_false", dest="do_use",
|
||||
help="Don't set new env as default for future operations.")
|
||||
def create(self, api, name, description=None,
|
||||
extras=None, spec=None, to_json=False, do_use=True):
|
||||
"""Create new environment."""
|
||||
spec = spec or {}
|
||||
if spec:
|
||||
with open(os.path.expanduser(spec), "rb") as f:
|
||||
spec = yaml.safe_load(f.read())
|
||||
if extras:
|
||||
extras = yaml.safe_load(extras)
|
||||
try:
|
||||
env = env_mgr.EnvManager.create(
|
||||
name, spec, description=description, extras=extras)
|
||||
except exceptions.ManagerInvalidSpec as e:
|
||||
_print("Env spec has wrong format:", to_json)
|
||||
_print(json.dumps(e.kwargs["spec"], indent=2), to_json)
|
||||
for err in e.kwargs["errors"]:
|
||||
_print(err, to_json)
|
||||
return 1
|
||||
except Exception:
|
||||
_print("Something went wrong during env creation:", to_json)
|
||||
_print(traceback.print_exc(), to_json)
|
||||
return 1
|
||||
|
||||
if do_use:
|
||||
self._use(env.uuid, to_json)
|
||||
self._show(env.data, to_json)
|
||||
return 0
|
||||
|
||||
@cliutils.args("--env", dest="env", type=str,
|
||||
metavar="<uuid>", required=False,
|
||||
help="UUID or name of the env.")
|
||||
@cliutils.args("--skip-cleanup", action="store_true", dest="skip_cleanup",
|
||||
help="Do not perform platforms cleanup before destroy.")
|
||||
@cliutils.args("--json", action="store_true", dest="to_json",
|
||||
help="Format output as JSON.")
|
||||
@cliutils.args("--detailed", action="store_true", dest="detailed",
|
||||
help="Show detailed information.")
|
||||
@envutils.with_default_env()
|
||||
def destroy(self, api, env=None, skip_cleanup=False, to_json=False,
|
||||
detailed=False):
|
||||
"""Destroy existing environment."""
|
||||
env = env_mgr.EnvManager.get(env)
|
||||
_print("Destroying %s" % env, to_json)
|
||||
result = env.destroy(skip_cleanup)
|
||||
return_code = int(result["destroy_info"]["skipped"])
|
||||
|
||||
if result["destroy_info"]["skipped"]:
|
||||
_print("%s Failed to destroy env %s: %s"
|
||||
% (NO, env, result["destroy_info"]["message"]), to_json)
|
||||
else:
|
||||
_print("%s Successfully destroyed env %s" % (YES, env), to_json)
|
||||
if detailed or to_json:
|
||||
print(json.dumps(result, indent=2))
|
||||
|
||||
return return_code
|
||||
|
||||
@cliutils.args("--env", dest="env", type=str,
|
||||
metavar="<uuid>", required=False,
|
||||
help="UUID or name of the env.")
|
||||
@cliutils.args("--force", action="store_true", dest="force",
|
||||
help="Delete DB records even if env is not destroyed.")
|
||||
@envutils.with_default_env()
|
||||
def delete(self, api, env=None, force=False):
|
||||
"""Deletes all records related to Env from db."""
|
||||
env_mgr.EnvManager.get(env).delete(force=force)
|
||||
# TODO(boris-42): clear env variables if default one is deleted
|
||||
|
||||
MSG_NO_ENVS = ("There are no environments. To create a new environment, "
|
||||
"use command bellow to create one:\nrally env create")
|
||||
|
||||
@cliutils.args("--json", action="store_true", dest="to_json",
|
||||
help="Format output as JSON.")
|
||||
@cliutils.suppress_warnings
|
||||
def list(self, api, to_json=False):
|
||||
"""List existing environments."""
|
||||
envs = env_mgr.EnvManager.list()
|
||||
if to_json:
|
||||
print(json.dumps([env.cached_data for env in envs], indent=2))
|
||||
elif not envs:
|
||||
print(self.MSG_NO_ENVS)
|
||||
else:
|
||||
cur_env = envutils.get_global(envutils.ENV_ENV)
|
||||
table = prettytable.PrettyTable()
|
||||
fields = ["uuid", "name", "status", "created_at", "description"]
|
||||
table.field_names = fields + ["default"]
|
||||
for env in envs:
|
||||
row = [env.cached_data[f] for f in fields]
|
||||
row.append(cur_env == env.cached_data["uuid"] and "*" or "")
|
||||
table.add_row(row)
|
||||
table.sortby = "created_at"
|
||||
table.reversesort = True
|
||||
table.align = "l"
|
||||
print(table.get_string())
|
||||
|
||||
def _show(self, env_data, to_json):
|
||||
if to_json:
|
||||
print(json.dumps(env_data, indent=2))
|
||||
else:
|
||||
table = prettytable.PrettyTable()
|
||||
table.header = False
|
||||
for k in ["uuid", "name", "status",
|
||||
"created_at", "updated_at", "description"]:
|
||||
table.add_row([k, env_data[k]])
|
||||
|
||||
table.add_row(["extras", json.dumps(env_data["extras"], indent=2)])
|
||||
for p, data in env_data["platforms"].items():
|
||||
table.add_row(["platform: %s" % p,
|
||||
json.dumps(data["platform_data"], indent=2)])
|
||||
table.align = "l"
|
||||
print(table.get_string())
|
||||
|
||||
@cliutils.args("--env", dest="env", type=str,
|
||||
metavar="<uuid>", required=False,
|
||||
help="UUID or name of the env.")
|
||||
@cliutils.args("--json", action="store_true", dest="to_json",
|
||||
help="Format output as JSON.")
|
||||
@cliutils.suppress_warnings
|
||||
@envutils.with_default_env()
|
||||
def show(self, api, env=None, to_json=False):
|
||||
env_data = env_mgr.EnvManager.get(env).data
|
||||
self._show(env_data, to_json)
|
||||
|
||||
@cliutils.args("--env", dest="env", type=str,
|
||||
metavar="<uuid>", required=False,
|
||||
help="UUID or name of the env.")
|
||||
@cliutils.args("--json", action="store_true", dest="to_json",
|
||||
help="Format output as JSON.")
|
||||
@envutils.with_default_env()
|
||||
def info(self, api, env=None, to_json=False):
|
||||
"""Show environment information."""
|
||||
env = env_mgr.EnvManager.get(env)
|
||||
env_info = env.get_info()
|
||||
return_code = int(any(v.get("error") for v in env_info.values()))
|
||||
|
||||
if to_json:
|
||||
print(json.dumps(env_info, indent=2))
|
||||
return return_code
|
||||
|
||||
table = prettytable.PrettyTable()
|
||||
table.field_names = ["platform", "info", "error"]
|
||||
for platform, data in env_info.items():
|
||||
table.add_row([
|
||||
platform, json.dumps(data["info"], indent=2),
|
||||
data.get("error") or ""
|
||||
])
|
||||
table.align = "l"
|
||||
print(env)
|
||||
print(table.get_string())
|
||||
return return_code
|
||||
|
||||
@cliutils.args("--env", dest="env", type=str,
|
||||
metavar="<uuid>", required=False,
|
||||
help="UUID or name of the env.")
|
||||
@cliutils.args("--json", action="store_true", dest="to_json",
|
||||
help="Format output as JSON.")
|
||||
@cliutils.args("--detailed", action="store_true", dest="detailed",
|
||||
help="Show detailed information.")
|
||||
@envutils.with_default_env()
|
||||
def check(self, api, env=None, to_json=False, detailed=False):
|
||||
"""Check availability of all platforms in environment."""
|
||||
env = env_mgr.EnvManager.get(env)
|
||||
data = env.check_health()
|
||||
available = all(x["available"] for x in data.values())
|
||||
|
||||
if to_json:
|
||||
print(json.dumps(data, indent=2))
|
||||
return not available
|
||||
|
||||
def _format_raw(plugin_name, el):
|
||||
return [
|
||||
el["available"] and YES or NO,
|
||||
plugin_name.split("@")[1], el["message"], plugin_name
|
||||
]
|
||||
|
||||
table = prettytable.PrettyTable()
|
||||
if detailed:
|
||||
table.field_names = ["Available", "Platform", "Message", "Plugin"]
|
||||
for plugin_name, r in data.items():
|
||||
table.add_row(_format_raw(plugin_name, r))
|
||||
else:
|
||||
table.field_names = ["Available", "Platform", "Message"]
|
||||
for plugin_name, r in data.items():
|
||||
table.add_row(_format_raw(plugin_name, r)[:3])
|
||||
|
||||
table.align = "l"
|
||||
table.align["available"] = "c"
|
||||
print("%s %s" % (env, available and YES or NO))
|
||||
print(table.get_string())
|
||||
if not available and detailed:
|
||||
for name, p_data in data.items():
|
||||
if p_data["available"]:
|
||||
continue
|
||||
print("-" * 4)
|
||||
print("Plugin %s raised exception:" % name)
|
||||
print("".join(p_data["traceback"]))
|
||||
|
||||
return not available
|
||||
|
||||
@cliutils.args("--env", dest="env", type=str,
|
||||
metavar="<uuid>", required=False,
|
||||
help="UUID or name of a env.")
|
||||
@cliutils.args("--json", action="store_true", dest="to_json",
|
||||
help="Format output as JSON.")
|
||||
def use(self, api, env, to_json=False):
|
||||
"""Set default environment."""
|
||||
try:
|
||||
env = env_mgr.EnvManager.get(env)
|
||||
except exceptions.DBRecordNotFound:
|
||||
_print("Can't use non existing environment %s." % env, to_json)
|
||||
return 1
|
||||
self._use(env.uuid, to_json)
|
||||
|
||||
def _use(self, env_uuid, to_json):
|
||||
_print("Using environment: %s" % env_uuid, to_json)
|
||||
fileutils.update_globals_file(envutils.ENV_ENV, env_uuid)
|
@ -22,11 +22,12 @@ from rally.common import fileutils
|
||||
from rally import exceptions
|
||||
|
||||
PATH_GLOBALS = "~/.rally/globals"
|
||||
ENV_ENV = "RALLY_ENV"
|
||||
ENV_DEPLOYMENT = "RALLY_DEPLOYMENT"
|
||||
ENV_TASK = "RALLY_TASK"
|
||||
ENV_VERIFIER = "RALLY_VERIFIER"
|
||||
ENV_VERIFICATION = "RALLY_VERIFICATION"
|
||||
ENVVARS = [ENV_DEPLOYMENT, ENV_TASK, ENV_VERIFIER, ENV_VERIFICATION]
|
||||
ENVVARS = [ENV_ENV, ENV_DEPLOYMENT, ENV_TASK, ENV_VERIFIER, ENV_VERIFICATION]
|
||||
|
||||
MSG_MISSING_ARG = "Missing argument: --%(arg_name)s"
|
||||
|
||||
@ -69,9 +70,31 @@ def default_from_global(arg_name, env_name,
|
||||
return decorator.decorator(default_from_global)
|
||||
|
||||
|
||||
def with_default_deployment(cli_arg_name="uuid"):
|
||||
def with_default_env():
|
||||
# NOTE(boris-42): This allows smooth transition from deployment to env
|
||||
# set ENV_ENV from ENV_DEPLOYMENT if ENV is not presented
|
||||
# This should be removed with rally env command
|
||||
if not os.environ.get(ENV_ENV):
|
||||
if os.environ.get(ENV_DEPLOYMENT):
|
||||
os.environ[ENV_ENV] = ENV_DEPLOYMENT
|
||||
|
||||
return default_from_global(
|
||||
"deployment", ENV_DEPLOYMENT, cli_arg_name,
|
||||
"env", ENV_ENV, "env",
|
||||
message="There is no default env. To set it use command:\n"
|
||||
"\trally env use <env_uuid>|<env_name>\n"
|
||||
"or pass uuid or name to your command using --%(arg_name)s")
|
||||
|
||||
|
||||
def with_default_deployment(cli_arg_name="uuid"):
|
||||
# NOTE(boris-42): This allows smooth transition from deployment to env
|
||||
# set ENV_ENV from ENV_DEPLOYMENT and use ENV_ENV
|
||||
# This should be removed with rally env command
|
||||
if not os.environ.get(ENV_ENV):
|
||||
if os.environ.get(ENV_DEPLOYMENT):
|
||||
os.environ[ENV_ENV] = ENV_DEPLOYMENT
|
||||
|
||||
return default_from_global(
|
||||
"deployment", ENV_ENV, cli_arg_name,
|
||||
message="There is no default deployment.\n"
|
||||
"\tPlease use command:\n"
|
||||
"\trally deployment use <deployment_uuid>|<deployment_name>\n"
|
||||
|
@ -22,6 +22,7 @@ import sys
|
||||
from rally.cli import cliutils
|
||||
from rally.cli.commands import db
|
||||
from rally.cli.commands import deployment
|
||||
from rally.cli.commands import env
|
||||
from rally.cli.commands import plugin
|
||||
from rally.cli.commands import task
|
||||
from rally.cli.commands import verify
|
||||
@ -29,6 +30,7 @@ from rally.cli.commands import verify
|
||||
|
||||
categories = {
|
||||
"db": db.DBCommands,
|
||||
"env": env.EnvCommands,
|
||||
"deployment": deployment.DeploymentCommands,
|
||||
"plugin": plugin.PluginCommands,
|
||||
"task": task.TaskCommands,
|
||||
@ -39,5 +41,6 @@ categories = {
|
||||
def main():
|
||||
return cliutils.run(sys.argv, categories)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
18
rally/env/env_mgr.py
vendored
18
rally/env/env_mgr.py
vendored
@ -359,12 +359,15 @@ class EnvManager(object):
|
||||
platform plugins and their arguments.
|
||||
:returns: EnvManager instance corresponding to created Env
|
||||
"""
|
||||
if description is not None:
|
||||
spec["!description"] = description
|
||||
if extras is not None:
|
||||
spec["!extras"] = extras
|
||||
if config is not None:
|
||||
spec["!config"] = config
|
||||
# NOTE(boris-42): this allows to avoid validation copy paste. If spec
|
||||
# is not dict it will fail during validation process
|
||||
if isinstance(spec, dict):
|
||||
if description is not None:
|
||||
spec["!description"] = description
|
||||
if extras is not None:
|
||||
spec["!extras"] = extras
|
||||
if config is not None:
|
||||
spec["!config"] = config
|
||||
|
||||
self = cls._validate_and_create_env(name, spec)
|
||||
self._create_platforms()
|
||||
@ -613,6 +616,9 @@ class EnvManager(object):
|
||||
|
||||
from rally.common import objects
|
||||
|
||||
# TODO(boris-42): This is breaking all kinds of rules of good
|
||||
# architecture, and we should remove this thing from
|
||||
# here...
|
||||
for verifier in objects.Verifier.list():
|
||||
verifier.set_env(self.uuid)
|
||||
verifier.manager.uninstall()
|
||||
|
@ -94,11 +94,6 @@ class ManagerInvalidSpec(ManagerException):
|
||||
error_code = 409
|
||||
msg_fmt = "%(mgr)s manager got invalid spec: \n%(errors)s"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
kwargs["errors"] = "\n".join(kwargs["errors"])
|
||||
self.spec = kwargs.pop("spec", "")
|
||||
super(ManagerInvalidSpec, self).__init__(**kwargs)
|
||||
|
||||
|
||||
class ManagerInvalidState(ManagerException):
|
||||
error_code = 500
|
||||
|
43
tests/functional/extra/fake_platforms.py
Normal file
43
tests/functional/extra/fake_platforms.py
Normal file
@ -0,0 +1,43 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from rally.env import platform
|
||||
|
||||
|
||||
@platform.configure(name="good", platform="fake")
|
||||
class GoodPlatform(platform.Platform):
|
||||
|
||||
CONFIG_SCHEMA = {}
|
||||
|
||||
def create(self):
|
||||
return {}, {}
|
||||
|
||||
def destroy(self):
|
||||
pass
|
||||
|
||||
def cleanup(self, task_uuid=None):
|
||||
return {
|
||||
"message": "Coming soon!",
|
||||
"discovered": 0,
|
||||
"deleted": 0,
|
||||
"failed": 0,
|
||||
"resources": {},
|
||||
"errors": []
|
||||
}
|
||||
|
||||
def check_health(self):
|
||||
return {"available": True}
|
||||
|
||||
def info(self):
|
||||
return {"info": {"a": 1}}
|
92
tests/functional/test_cli_env.py
Normal file
92
tests/functional/test_cli_env.py
Normal file
@ -0,0 +1,92 @@
|
||||
# Copyright 2013: ITLook, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import unittest
|
||||
|
||||
from tests.functional import utils
|
||||
|
||||
|
||||
class EnvTestCase(unittest.TestCase):
|
||||
|
||||
def test_create_no_spec(self):
|
||||
rally = utils.Rally()
|
||||
rally("env create --name empty --description de")
|
||||
self.assertIn("empty", rally("env list"))
|
||||
env_data = rally("env show --json", getjson=True)
|
||||
self.assertEqual("empty", env_data["name"])
|
||||
self.assertEqual("de", env_data["description"])
|
||||
self.assertEqual({}, env_data["extras"])
|
||||
self.assertEqual({}, env_data["platforms"])
|
||||
|
||||
def _create_spec(self, spec):
|
||||
f = tempfile.NamedTemporaryFile(delete=False)
|
||||
|
||||
def unlink():
|
||||
os.unlink(f.name)
|
||||
|
||||
self.addCleanup(unlink)
|
||||
|
||||
f.write(json.dumps(spec, indent=2))
|
||||
f.close()
|
||||
return f.name
|
||||
|
||||
def test_create_check_info_destroy_delete_with_spec(self):
|
||||
rally = utils.Rally(plugin_path="tests/functional/extra")
|
||||
|
||||
spec = self._create_spec({"good@fake": {}})
|
||||
rally("env create --name real --spec %s" % spec)
|
||||
env = rally("env show --json", getjson=True)
|
||||
self.assertIn("fake", env["platforms"])
|
||||
|
||||
env_info = rally("env info --json", getjson=True)
|
||||
self.assertEqual({"good@fake": {"info": {"a": 1}}}, env_info)
|
||||
|
||||
rally("env check --json")
|
||||
|
||||
def test_list_empty(self):
|
||||
rally = utils.Rally()
|
||||
# TODO(boris-42): Clean this up somehow
|
||||
rally("env destroy MAIN")
|
||||
rally("env delete MAIN")
|
||||
self.assertEqual([], rally("env list --json", getjson=True))
|
||||
self.assertIn("There are no environments", rally("env list"))
|
||||
|
||||
def test_list(self):
|
||||
rally = utils.Rally()
|
||||
envs = rally("env list --json", getjson=True)
|
||||
self.assertEqual(1, len(envs))
|
||||
self.assertEqual("MAIN", envs[0]["name"])
|
||||
self.assertIn("MAIN", rally("env list"))
|
||||
|
||||
def test_use(self):
|
||||
|
||||
def show_helper():
|
||||
return rally("env show --json", getjson=True)
|
||||
|
||||
rally = utils.Rally()
|
||||
self.assertEqual("MAIN", show_helper()["name"])
|
||||
empty_uuid = rally("env create --name empty --json",
|
||||
getjson=True)["uuid"]
|
||||
self.assertEqual("empty", show_helper()["name"])
|
||||
rally("env use MAIN")
|
||||
self.assertEqual("MAIN", show_helper()["name"])
|
||||
rally("env use %s" % empty_uuid)
|
||||
self.assertEqual("empty", show_helper()["name"])
|
||||
rally("env create --name empty2 --description de --no-use")
|
||||
self.assertEqual("empty", show_helper()["name"])
|
@ -186,16 +186,13 @@ class Rally(object):
|
||||
if not isinstance(cmd, list):
|
||||
cmd = cmd.split(" ")
|
||||
try:
|
||||
if getjson:
|
||||
if no_logs or getjson:
|
||||
cmd = self.args + ["--log-file", "/dev/null"] + cmd
|
||||
else:
|
||||
cmd = self.args + cmd
|
||||
|
||||
if no_logs:
|
||||
with open(os.devnull, "w") as DEVNULL:
|
||||
output = encodeutils.safe_decode(subprocess.check_output(
|
||||
cmd, stderr=DEVNULL, env=self.env))
|
||||
else:
|
||||
cmd = self.args + cmd
|
||||
output = encodeutils.safe_decode(subprocess.check_output(
|
||||
cmd, stderr=subprocess.STDOUT, env=self.env))
|
||||
|
||||
|
@ -298,10 +298,14 @@ class DeploymentCommandsTestCase(test.TestCase):
|
||||
with mock.patch("rally.cli.commands.deployment.open", mock.mock_open(),
|
||||
create=True) as mock_file:
|
||||
self.deployment.use(self.fake_api, deployment_id)
|
||||
self.assertEqual(2, mock_path_exists.call_count)
|
||||
mock_update_env_file.assert_called_once_with(os.path.expanduser(
|
||||
"~/.rally/globals"),
|
||||
"RALLY_DEPLOYMENT", "%s\n" % deployment_id)
|
||||
self.assertEqual(3, mock_path_exists.call_count)
|
||||
mock_update_env_file.assert_has_calls([
|
||||
mock.call(os.path.expanduser("~/.rally/globals"),
|
||||
"RALLY_DEPLOYMENT", "%s\n" % deployment_id),
|
||||
mock.call(os.path.expanduser("~/.rally/globals"),
|
||||
"RALLY_ENV", "%s\n" % deployment_id),
|
||||
])
|
||||
|
||||
mock_file.return_value.write.assert_any_call(
|
||||
"export OS_ENDPOINT='fake_endpoint'\n")
|
||||
mock_file.return_value.write.assert_any_call(
|
||||
@ -340,10 +344,13 @@ class DeploymentCommandsTestCase(test.TestCase):
|
||||
with mock.patch("rally.cli.commands.deployment.open", mock.mock_open(),
|
||||
create=True) as mock_file:
|
||||
self.deployment.use(self.fake_api, deployment_id)
|
||||
self.assertEqual(2, mock_path_exists.call_count)
|
||||
mock_update_env_file.assert_called_once_with(os.path.expanduser(
|
||||
"~/.rally/globals"),
|
||||
"RALLY_DEPLOYMENT", "%s\n" % deployment_id)
|
||||
self.assertEqual(3, mock_path_exists.call_count)
|
||||
mock_update_env_file.assert_has_calls([
|
||||
mock.call(os.path.expanduser("~/.rally/globals"),
|
||||
"RALLY_DEPLOYMENT", "%s\n" % deployment_id),
|
||||
mock.call(os.path.expanduser("~/.rally/globals"),
|
||||
"RALLY_ENV", "%s\n" % deployment_id)
|
||||
])
|
||||
mock_file.return_value.write.assert_any_call(
|
||||
"export OS_ENDPOINT='fake_endpoint'\n")
|
||||
mock_file.return_value.write.assert_any_call(
|
||||
@ -376,8 +383,10 @@ class DeploymentCommandsTestCase(test.TestCase):
|
||||
self.assertIsNone(status)
|
||||
self.fake_api.deployment.get.assert_called_once_with(
|
||||
deployment="fake_name")
|
||||
mock_update_globals_file.assert_called_once_with(
|
||||
envutils.ENV_DEPLOYMENT, "fake_uuid")
|
||||
mock_update_globals_file.assert_has_calls([
|
||||
mock.call(envutils.ENV_DEPLOYMENT, "fake_uuid"),
|
||||
mock.call(envutils.ENV_ENV, "fake_uuid")
|
||||
])
|
||||
mock__update_openrc_deployment_file.assert_called_once_with(
|
||||
"fake_uuid", "foo_admin")
|
||||
|
||||
|
375
tests/unit/cli/commands/test_env.py
Normal file
375
tests/unit/cli/commands/test_env.py
Normal file
@ -0,0 +1,375 @@
|
||||
# Copyright 2018: ITLook Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime as dt
|
||||
import json
|
||||
import uuid
|
||||
|
||||
import mock
|
||||
|
||||
from rally.cli.commands import env
|
||||
from rally.env import env_mgr
|
||||
from rally import exceptions
|
||||
from tests.unit import test
|
||||
|
||||
|
||||
class EnvCommandsTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(EnvCommandsTestCase, self).setUp()
|
||||
self.env = env.EnvCommands()
|
||||
# TODO(boris-42): api argument is not used by EvnCommands
|
||||
# it's going to be removed when we remove rally.api
|
||||
# from other commands
|
||||
self.api = None
|
||||
|
||||
@staticmethod
|
||||
def gen_env_data(uid=None, name=None, description=None,
|
||||
status=env_mgr.STATUS.INIT, spec=None, extras=None):
|
||||
return {
|
||||
"uuid": uid or str(uuid.uuid4()),
|
||||
"created_at": dt.datetime(2017, 1, 1),
|
||||
"updated_at": dt.datetime(2017, 1, 2),
|
||||
"name": name or str(uuid.uuid4()),
|
||||
"description": description or str(uuid.uuid4()),
|
||||
"status": status,
|
||||
"spec": spec or {},
|
||||
"extras": extras or {}
|
||||
}
|
||||
|
||||
@mock.patch("rally.cli.commands.env.print")
|
||||
def test__print(self, mock_print):
|
||||
env._print("Test42", silent=True)
|
||||
self.assertFalse(mock_print.called)
|
||||
env._print("Test42", silent=False)
|
||||
mock_print.assert_called_once_with("Test42")
|
||||
env._print("Test43")
|
||||
mock_print.assert_has_calls([mock.call("Test42"), mock.call("Test43")])
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.create")
|
||||
@mock.patch("rally.cli.commands.env.EnvCommands._show")
|
||||
def test_create_emtpy_use(self, mock_env_commands__show,
|
||||
mock_env_manager_create):
|
||||
self.assertEqual(
|
||||
0, self.env.create(self.api, "test_name", "test_description"))
|
||||
mock_env_manager_create.assert_called_once_with(
|
||||
"test_name", {}, description="test_description", extras=None)
|
||||
mock_env_commands__show.assert_called_once_with(
|
||||
mock_env_manager_create.return_value.data, False)
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.create")
|
||||
@mock.patch("rally.cli.commands.env.open", create=True)
|
||||
@mock.patch("rally.cli.commands.env.print")
|
||||
def test_create_spec_and_extra_no_use_to_json(self, mock_print, mock_open,
|
||||
mock_env_manager_create):
|
||||
mock_open.side_effect = mock.mock_open(read_data="{\"a\": 1}")
|
||||
mock_env_manager_create.return_value.data = {"test": "test"}
|
||||
self.assertEqual(
|
||||
0, self.env.create(self.api, "n", "d", extras="{\"extra\": 123}",
|
||||
spec="spec.yml", to_json=True, do_use=False))
|
||||
|
||||
mock_env_manager_create.assert_called_once_with(
|
||||
"n", {"a": 1}, description="d", extras={"extra": 123})
|
||||
mock_print.assert_called_once_with(
|
||||
json.dumps(mock_env_manager_create.return_value.data, indent=2))
|
||||
|
||||
@mock.patch("rally.cli.commands.env.print")
|
||||
@mock.patch("rally.cli.commands.env.open", create=True)
|
||||
def test_create_invalid_spec(self, mock_open, mock_print):
|
||||
mock_open.side_effect = mock.mock_open(read_data="[]")
|
||||
self.assertEqual(
|
||||
1, self.env.create(self.api, "n", "d", spec="spec.yml"))
|
||||
mock_print.assert_has_calls([
|
||||
mock.call("Env spec has wrong format:"),
|
||||
mock.call("[]"),
|
||||
mock.call(mock.ANY)
|
||||
])
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.create")
|
||||
@mock.patch("rally.cli.commands.env.print")
|
||||
def test_create_exception(self, mock_print, mock_env_manager_create):
|
||||
mock_env_manager_create.side_effect = Exception
|
||||
self.assertEqual(1, self.env.create(self.api, "n", "d"))
|
||||
mock_print.assert_has_calls([
|
||||
mock.call("Something went wrong during env creation:"),
|
||||
mock.call(mock.ANY)
|
||||
])
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.get")
|
||||
@mock.patch("rally.cli.commands.env.print")
|
||||
def test_destroy(self, mock_print, mock_env_manager_get):
|
||||
env_ = mock.Mock()
|
||||
env_inst = mock_env_manager_get.return_value
|
||||
env_inst.destroy.return_value = {
|
||||
"destroy_info": {
|
||||
"skipped": True,
|
||||
"message": "42"
|
||||
}
|
||||
}
|
||||
self.assertEqual(1, self.env.destroy(self.api, env_))
|
||||
mock_env_manager_get.assert_called_once_with(env_)
|
||||
env_inst.destroy.assert_called_once_with(False)
|
||||
mock_print.assert_has_calls([
|
||||
mock.call("Destroying %s" % env_inst),
|
||||
mock.call(":-( Failed to destroy env %s: 42" % env_inst)
|
||||
])
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.get")
|
||||
@mock.patch("rally.cli.commands.env.print")
|
||||
def test_destroy_to_json(self, mock_print, mock_env_manager_get):
|
||||
env_ = mock.Mock()
|
||||
env_inst = mock_env_manager_get.return_value
|
||||
|
||||
env_inst.destroy.return_value = {
|
||||
"cleanup_info": {
|
||||
"skipped": False
|
||||
},
|
||||
"destroy_info": {
|
||||
"skipped": False,
|
||||
"message": "42"
|
||||
}
|
||||
}
|
||||
self.assertEqual(
|
||||
0,
|
||||
self.env.destroy(self.api, env_, skip_cleanup=True, to_json=True))
|
||||
env_inst.destroy.assert_called_once_with(True)
|
||||
mock_print.assert_called_once_with(
|
||||
json.dumps(env_inst.destroy.return_value, indent=2))
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.get")
|
||||
def test_delete(self, mock_env_manager_get):
|
||||
env_ = mock.Mock()
|
||||
self.env.delete(self.api, env_)
|
||||
mock_env_manager_get.assert_called_once_with(env_)
|
||||
mock_env_manager_get.return_value.delete.assert_called_once_with(
|
||||
force=False)
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.get")
|
||||
def test_delete_force(self, mock_env_manager_get):
|
||||
env_ = mock.Mock()
|
||||
self.env.delete(self.api, env_, force=True)
|
||||
mock_env_manager_get.assert_called_once_with(env_)
|
||||
mock_env_manager_get.return_value.delete.assert_called_once_with(
|
||||
force=True)
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.list")
|
||||
@mock.patch("rally.cli.commands.env.print")
|
||||
def test_list_empty(self, mock_print, mock_env_manager_list):
|
||||
mock_env_manager_list.return_value = []
|
||||
self.env.list(self.api, to_json=True)
|
||||
mock_print.assert_called_once_with("[]")
|
||||
mock_print.reset_mock()
|
||||
self.env.list(self.api, to_json=False)
|
||||
mock_print.assert_called_once_with(self.env.MSG_NO_ENVS)
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.list")
|
||||
@mock.patch("rally.cli.commands.env.print")
|
||||
def test_list(self, mock_print, mock_env_manager_list):
|
||||
env_a = env_mgr.EnvManager(self.gen_env_data())
|
||||
env_b = env_mgr.EnvManager(self.gen_env_data())
|
||||
mock_env_manager_list.return_value = [env_a, env_b]
|
||||
|
||||
self.env.list(self.api, to_json=True)
|
||||
mock_env_manager_list.assert_called_once_with()
|
||||
mock_print.assert_called_once_with(
|
||||
json.dumps([env_a.cached_data, env_b.cached_data], indent=2))
|
||||
|
||||
for m in [mock_env_manager_list, mock_print]:
|
||||
m.reset_mock()
|
||||
|
||||
self.env.list(self.api)
|
||||
mock_env_manager_list.assert_called_once_with()
|
||||
mock_print.assert_called_once_with(mock.ANY)
|
||||
|
||||
@mock.patch("rally.cli.commands.env.print")
|
||||
def test__show(self, mock_print):
|
||||
env_data = self.gen_env_data(
|
||||
uid="a77004a6-7fe5-4b75-a278-009c3c5f6b20",
|
||||
name="my best env",
|
||||
description="description")
|
||||
env_data["platforms"] = {}
|
||||
self.env._show(env_data, False)
|
||||
mock_print.assert_called_once_with(
|
||||
"+-------------+--------------------------------------+\n"
|
||||
"| uuid | a77004a6-7fe5-4b75-a278-009c3c5f6b20 |\n"
|
||||
"| name | my best env |\n"
|
||||
"| status | INITIALIZING |\n"
|
||||
"| created_at | 2017-01-01 00:00:00 |\n"
|
||||
"| updated_at | 2017-01-02 00:00:00 |\n"
|
||||
"| description | description |\n"
|
||||
"| extras | {} |\n"
|
||||
"+-------------+--------------------------------------+")
|
||||
|
||||
@mock.patch("rally.cli.commands.env.print")
|
||||
def test__show_to_json(self, mock_print):
|
||||
self.env._show("data", True)
|
||||
mock_print.assert_called_once_with("\"data\"")
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.get")
|
||||
@mock.patch("rally.cli.commands.env.EnvCommands._show")
|
||||
def test_show(self, mock__show, mock_env_manager_get):
|
||||
env_ = mock.Mock()
|
||||
self.env.show(self.api, env_)
|
||||
mock_env_manager_get.assert_called_once_with(env_)
|
||||
mock__show.assert_called_once_with(
|
||||
mock_env_manager_get.return_value.data, False)
|
||||
mock__show.reset_mock()
|
||||
self.env.show(self.api, env_, to_json=True)
|
||||
mock__show.assert_called_once_with(
|
||||
mock_env_manager_get.return_value.data, True)
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.get")
|
||||
@mock.patch("rally.cli.commands.env.print")
|
||||
def test_info_to_json(self, mock_print, mock_env_manager_get):
|
||||
mock_env_manager_get.return_value.get_info.return_value = {
|
||||
"p1": {"info": {"a": True}}}
|
||||
|
||||
self.assertEqual(0, self.env.info(self.api, "any", to_json=True))
|
||||
mock_env_manager_get.assert_called_once_with("any")
|
||||
mock_print.assert_called_once_with(
|
||||
json.dumps(mock_env_manager_get.return_value.get_info.return_value,
|
||||
indent=2)
|
||||
)
|
||||
mock_env_manager_get.return_value.get_info.return_value = {
|
||||
"p1": {"info": {"a": False}},
|
||||
"p2": {"info": {}, "error": "some error"}
|
||||
}
|
||||
self.assertEqual(1, self.env.info(self.api, "any", to_json=True))
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.get")
|
||||
@mock.patch("rally.cli.commands.env.print")
|
||||
def test_info(self, mock_print, mock_env_manager_get):
|
||||
mock_env_manager_get.return_value.get_info.return_value = {
|
||||
"p1@pl1": {"info": {"a": False}},
|
||||
"p2@pl2": {"info": {}, "error": "some error"}
|
||||
}
|
||||
self.assertEqual(1, self.env.info(self.api, "any"))
|
||||
mock_print.assert_has_calls([
|
||||
mock.call(mock_env_manager_get.return_value),
|
||||
mock.call(
|
||||
"+----------+--------------+------------+\n"
|
||||
"| platform | info | error |\n"
|
||||
"+----------+--------------+------------+\n"
|
||||
"| p1@pl1 | { | |\n"
|
||||
"| | \"a\": false | |\n"
|
||||
"| | } | |\n"
|
||||
"| p2@pl2 | {} | some error |\n"
|
||||
"+----------+--------------+------------+"
|
||||
)
|
||||
])
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.get")
|
||||
@mock.patch("rally.cli.commands.env.print")
|
||||
def test_check(self, mock_print, mock_env_manager_get):
|
||||
mock_env_manager_get.return_value.check_health.return_value = {
|
||||
"p1@p1": {"available": True, "message": "OK!"},
|
||||
"p2@p2": {"available": False, "message": "BAD !"}
|
||||
}
|
||||
self.assertEqual(1, self.env.check(self.api, "env_42"))
|
||||
mock_env_manager_get.assert_called_once_with("env_42")
|
||||
|
||||
mock_print.assert_has_calls([
|
||||
mock.call("%s :-(" % mock_env_manager_get.return_value),
|
||||
mock.call(
|
||||
"+-----------+----------+---------+\n"
|
||||
"| Available | Platform | Message |\n"
|
||||
"+-----------+----------+---------+\n"
|
||||
"| :-( | p2 | BAD ! |\n"
|
||||
"| :-) | p1 | OK! |\n"
|
||||
"+-----------+----------+---------+"
|
||||
)
|
||||
])
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.get")
|
||||
@mock.patch("rally.cli.commands.env.print")
|
||||
def test_check_detailed(self, mock_print, mock_env_manager_get):
|
||||
mock_env_manager_get.return_value.check_health.return_value = {
|
||||
"p1@p1": {"available": True, "message": "OK!"},
|
||||
"p2@p2": {"available": False, "message": "BAD !",
|
||||
"traceback": "Filaneme\n Codeline\nError"}
|
||||
}
|
||||
self.assertEqual(1, self.env.check(self.api, "env_42", detailed=True))
|
||||
mock_env_manager_get.assert_called_once_with("env_42")
|
||||
|
||||
print(mock_print.call_args_list)
|
||||
mock_print.assert_has_calls([
|
||||
mock.call("%s :-(" % mock_env_manager_get.return_value),
|
||||
mock.call(
|
||||
"+-----------+----------+---------+--------+\n"
|
||||
"| Available | Platform | Message | Plugin |\n"
|
||||
"+-----------+----------+---------+--------+\n"
|
||||
"| :-( | p2 | BAD ! | p2@p2 |\n"
|
||||
"| :-) | p1 | OK! | p1@p1 |\n"
|
||||
"+-----------+----------+---------+--------+"
|
||||
),
|
||||
mock.call("----"),
|
||||
mock.call("Plugin p2@p2 raised exception:"),
|
||||
mock.call("Filaneme\n Codeline\nError")
|
||||
])
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.get")
|
||||
@mock.patch("rally.cli.commands.env.print")
|
||||
def test_check_to_json(self, mock_print, mock_env_manager_get):
|
||||
mock_env_manager_get.return_value.check_health.return_value = {
|
||||
"p1": {"available": True}}
|
||||
|
||||
self.assertEqual(0, self.env.check(self.api, "some_env", to_json=True))
|
||||
mock_env_manager_get.assert_called_once_with("some_env")
|
||||
mock_print.assert_called_once_with(
|
||||
json.dumps(
|
||||
mock_env_manager_get.return_value.check_health.return_value,
|
||||
indent=2)
|
||||
)
|
||||
|
||||
mock_env_manager_get.return_value.check_health.return_value = {
|
||||
"p1": {"available": False}}
|
||||
self.assertEqual(1, self.env.check(self.api, "some_env", to_json=True))
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.get")
|
||||
@mock.patch("rally.cli.commands.env._print")
|
||||
def test_use_not_found(self, mock__print, mock_env_manager_get):
|
||||
mock_env_manager_get.side_effect = exceptions.DBRecordNotFound(
|
||||
criteria="", table="")
|
||||
env_ = str(uuid.uuid4())
|
||||
self.assertEqual(1, self.env.use(self.api, env_))
|
||||
mock_env_manager_get.assert_called_once_with(env_)
|
||||
mock__print.assert_called_once_with(
|
||||
"Can't use non existing environment %s." % env_, False)
|
||||
|
||||
@mock.patch("rally.env.env_mgr.EnvManager.get")
|
||||
@mock.patch("rally.cli.commands.env.EnvCommands._use")
|
||||
def test_use(self, mock__use, mock_env_manager_get):
|
||||
mock_env_manager_get.side_effect = [
|
||||
mock.Mock(uuid="aa"), mock.Mock(uuid="bb")
|
||||
]
|
||||
self.assertIsNone(self.env.use(self.api, "aa"))
|
||||
self.assertIsNone(self.env.use(self.api, "bb", to_json=True))
|
||||
|
||||
mock_env_manager_get.assert_has_calls(
|
||||
[mock.call("aa"), mock.call("bb")])
|
||||
mock__use.assert_has_calls(
|
||||
[mock.call("aa", False), mock.call("bb", True)])
|
||||
|
||||
@mock.patch("rally.cli.commands.env.fileutils.update_globals_file")
|
||||
@mock.patch("rally.cli.commands.env.print")
|
||||
def test__use(self, mock_print, mock_update_globals_file):
|
||||
self.env._use("aa", True)
|
||||
self.assertFalse(mock_print.called)
|
||||
mock_update_globals_file.assert_called_once_with("RALLY_ENV", "aa")
|
||||
mock_update_globals_file.reset_mock()
|
||||
|
||||
self.env._use("bb", False)
|
||||
mock_print.assert_called_once_with("Using environment: bb")
|
||||
mock_update_globals_file.assert_called_once_with("RALLY_ENV", "bb")
|
Loading…
Reference in New Issue
Block a user