From e645436354dd77330d3e40a96664f553cf2cd2fa Mon Sep 17 00:00:00 2001 From: abhijeetkaurav1st Date: Tue, 9 Nov 2021 17:56:51 +0530 Subject: [PATCH 1/4] Fixing project update and adding account addition through cli switches --- calm/dsl/api/project.py | 9 ++ calm/dsl/cli/project_commands.py | 22 ++++ calm/dsl/cli/projects.py | 171 ++++++++++++++++++++++++++++++- 3 files changed, 201 insertions(+), 1 deletion(-) diff --git a/calm/dsl/api/project.py b/calm/dsl/api/project.py index d3e826a21..a9e2f6427 100644 --- a/calm/dsl/api/project.py +++ b/calm/dsl/api/project.py @@ -9,6 +9,7 @@ def __init__(self, connection): self.CALM_PROJECTS_PREFIX = ResourceAPI.ROOT + "/calm_projects" self.CALM_PROJECTS_ITEM = self.CALM_PROJECTS_PREFIX + "/{}" self.CALM_PROJECTS_PENDING_TASKS = self.CALM_PROJECTS_ITEM + "/pending_tasks/{}" + self.CALM_PROJECTS_USAGE = self.CALM_PROJECTS_ITEM + "/usage" def create(self, payload): @@ -31,6 +32,14 @@ def create(self, payload): return super().create(payload) + def usage(self, uuid, payload): + return self.connection._call( + self.CALM_PROJECTS_USAGE.format(uuid), + verify=False, + request_json=payload, + method=REQUEST.METHOD.POST, + ) + def delete(self, uuid): return self.connection._call( self.CALM_PROJECTS_ITEM.format(uuid), diff --git a/calm/dsl/cli/project_commands.py b/calm/dsl/cli/project_commands.py index fc2ff7b7f..ce82f27bf 100644 --- a/calm/dsl/cli/project_commands.py +++ b/calm/dsl/cli/project_commands.py @@ -138,6 +138,22 @@ def _describe_project(project_name, out): multiple=True, default=[], ) +@click.option( + "--add_account", + "-aa", + "add_account_list", + help="name of account to be added", + multiple=True, + default=[], +) +@click.option( + "--remove_account", + "-ra", + "remove_account_list", + help="name of account to be removed", + multiple=True, + default=[], +) @click.option( "--remove_user", "-ru", @@ -159,6 +175,8 @@ def _update_project( project_file, add_user_list, add_group_list, + add_account_list, + remove_account_list, remove_user_list, remove_group_list, ): @@ -177,6 +195,8 @@ def _update_project( project_file or add_user_list or add_group_list + or add_account_list + or remove_account_list or remove_user_list or remove_group_list ): @@ -201,4 +221,6 @@ def _update_project( add_group_list=add_group_list, remove_user_list=remove_user_list, remove_group_list=remove_group_list, + add_account_list=add_account_list, + remove_account_list=remove_account_list, ) diff --git a/calm/dsl/cli/projects.py b/calm/dsl/cli/projects.py index 47bd8a538..a79d3eca7 100644 --- a/calm/dsl/cli/projects.py +++ b/calm/dsl/cli/projects.py @@ -650,6 +650,23 @@ def update_project_from_dsl(project_name, project_file): if _group["name"] not in updated_project_groups_list: acp_remove_group_list.append(_group["name"]) + # Environment updation is not allowed, so adding existing environments + old_env_refs = old_project_payload["spec"]["resources"].get( + "environment_reference_list", [] + ) + if old_env_refs: + project_payload["spec"]["resources"][ + "environment_reference_list" + ] = old_env_refs + + default_env_ref = old_project_payload["spec"]["resources"].get( + "default_environment_reference", {} + ) + if default_env_ref: + project_payload["spec"]["resources"][ + "default_environment_reference" + ] = default_env_ref + # Setting correct metadata for update call project_payload["metadata"] = old_project_payload["metadata"] @@ -696,7 +713,13 @@ def update_project_from_dsl(project_name, project_file): def update_project_using_cli_switches( - project_name, add_user_list, add_group_list, remove_user_list, remove_group_list + project_name, + add_user_list, + add_group_list, + add_account_list, + remove_account_list, + remove_user_list, + remove_group_list, ): client = get_api_client() @@ -718,6 +741,9 @@ def update_project_using_cli_switches( project_payload = res.json() project_payload.pop("status", None) + project_usage_payload = { + "filter": {"account_reference_list": [], "subnet_reference_list": []} + } project_resources = project_payload["spec"]["resources"] project_users = [] project_groups = [] @@ -784,6 +810,71 @@ def update_project_using_cli_switches( "external_user_group_reference_list" ] = updated_group_reference_list + # Updating accounts data + if not set(add_account_list).isdisjoint(set(remove_account_list)): + LOG.error( + "Same accounts found in both added and removing list {}".format( + set(add_account_list).intersection(set(remove_account_list)) + ) + ) + sys.exit("Same accounts found in both added and removing list") + + project_accounts = project_resources.get("account_reference_list", []) + updated_proj_accounts = [] + for _acc in project_accounts: + _acc_uuid = _acc["uuid"] + account_cache_data = Cache.get_entity_data_using_uuid( + entity_type="account", uuid=_acc_uuid + ) + if not account_cache_data: + LOG.error( + "Account (uuid={}) not found. Please update cache".format(_acc_uuid) + ) + sys.exit("Account (uuid={}) not found".format(_acc_uuid)) + + if account_cache_data["name"] not in remove_account_list: + updated_proj_accounts.append(_acc) + else: + project_usage_payload["filter"]["account_reference_list"].append(_acc_uuid) + + project_account_uuids = [_e["uuid"] for _e in updated_proj_accounts] + for _acc in add_account_list: + account_cache_data = Cache.get_entity_data(entity_type="account", name=_acc) + if not account_cache_data: + LOG.error("Account (name={}) not found. Please update cache".format(_acc)) + sys.exit("Account (name={}) not found".format(_acc)) + + # Account already present + if account_cache_data["uuid"] in project_account_uuids: + continue + + updated_proj_accounts.append( + {"kind": "account", "name": _acc, "uuid": account_cache_data["uuid"]} + ) + + project_resources["account_reference_list"] = updated_proj_accounts + + LOG.info("Checking project usage") + res, err = client.project.usage(project_uuid, project_usage_payload) + if err: + LOG.error(err) + sys.exit(-1) + + project_usage = res.json() + msg_list = [] + should_update_project = is_project_updation_allowed(project_usage, msg_list) + if not should_update_project: + LOG.error("Project updation failed") + click.echo("\n".join(msg_list)) + click.echo( + json.dumps( + project_usage["status"].get("resources", {}), + indent=4, + separators=(",", ": "), + ) + ) + sys.exit(-1) + LOG.info("Updating project '{}'".format(project_name)) res, err = client.project.update(project_uuid, project_payload) if err: @@ -856,3 +947,81 @@ def remove_users_from_project_acps(project_uuid, remove_user_list, remove_group_ watch_project_task( project_uuid, res["status"]["execution_context"]["task_uuid"], poll_interval=4 ) + + +def is_project_updation_allowed(project_usage, msg_list): + """ + Returns whether project update is allowed. + Will also update project_usage dict to contain only associate entities + Args: + project_usage (dict): project usage details + Returns: + _eusage (bool): is updation allowed + """ + + def is_entity_used(e_usage): + + entity_used = False + app_cnt = e_usage.pop("app", 0) + if app_cnt: + entity_used = True + e_usage["app"] = app_cnt + + brownfield_cnt = e_usage.get("blueprint", {}).pop("brownfield", 0) + greenfield_cnt = e_usage.get("blueprint", {}).pop("greenfield", 0) + if brownfield_cnt or greenfield_cnt: + entity_used = True + if brownfield_cnt: + e_usage["blueprint"]["brownfield"] = brownfield_cnt + if greenfield_cnt: + e_usage["blueprint"]["greenfield"] = greenfield_cnt + else: + e_usage.pop("blueprint", None) + + endpoint_cnt = e_usage.pop("endpoint", 0) + if endpoint_cnt: + entity_used = True + e_usage["endpoint"] = endpoint_cnt + + environment_cnt = e_usage.pop("environment", 0) + if environment_cnt: + entity_used = True + e_usage["environment"] = environment_cnt + + runbook_cnt = e_usage.pop("runbook", 0) + if runbook_cnt: + entity_used = True + e_usage["runbook"] = runbook_cnt + + return entity_used + + updation_allowed = True + accounts_usage = project_usage["status"]["resources"].get("account_list", []) + for _ac in accounts_usage: + entity_used = is_entity_used(_ac["usage"]) + if entity_used: + updation_allowed = False + account_cache_data = Cache.get_entity_data_using_uuid( + entity_type="account", uuid=_ac["uuid"] + ) + msg_list.append( + "Please disassociate the account '{}' (uuid='{}') references from existing entities".format( + account_cache_data["name"], account_cache_data["uuid"] + ) + ) + + subnets_usage = project_usage["status"]["resources"].get("subnet_list", []) + for _snt in subnets_usage: + entity_used = is_entity_used(_snt["usage"]) + if entity_used: + updation_allowed = False + subnet_cache_data = Cache.get_entity_data_using_uuid( + entity_type=CACHE.ENTITY.AHV_SUBNET, uuid=_snt["uuid"] + ) + msg_list.append( + "Please disassociate the subnet '{}' (uuid='{}') references from existing entities".format( + subnet_cache_data["name"], subnet_cache_data["uuid"] + ) + ) + + return updation_allowed From 1471c9eb4fb62fb7f9eee69e0ee7327464f9a36c Mon Sep 17 00:00:00 2001 From: abhijeetkaurav1st Date: Tue, 9 Nov 2021 17:57:45 +0530 Subject: [PATCH 2/4] Adding test for accoun updation through cli switches --- tests/cli/test_project_commands.py | 64 ++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/cli/test_project_commands.py b/tests/cli/test_project_commands.py index cacd22e69..31bd1438c 100644 --- a/tests/cli/test_project_commands.py +++ b/tests/cli/test_project_commands.py @@ -22,6 +22,10 @@ USER = DSL_CONFIG["USERS"][0] USER_NAME = USER["NAME"] +ACCOUNTS = DSL_CONFIG["ACCOUNTS"] +AWS_ACCOUNT = ACCOUNTS["AWS"][0] +AWS_ACCOUNT_NAME = AWS_ACCOUNT["NAME"] + # calm_version CALM_VERSION = Version.get_version("Calm") @@ -249,6 +253,66 @@ def _test_update_project_using_cli_switches(self): pytest.fail("Project update call failed") LOG.info("Success") + def _test_account_updation_using_cli_switches(self): + """Removes and adds account to the project""" + + runner = CliRunner() + LOG.info( + "Testing 'calm update project' command using cli switches for account deletion" + ) + result = runner.invoke( + cli, + [ + "update", + "project", + self.dsl_project_name, + "--remove_account", + AWS_ACCOUNT_NAME, + ], + ) + if result.exit_code: + cli_res_dict = {"Output": result.output, "Exception": str(result.exception)} + LOG.debug( + "Cli Response: {}".format( + json.dumps(cli_res_dict, indent=4, separators=(",", ": ")) + ) + ) + LOG.debug( + "Traceback: \n{}".format( + "".join(traceback.format_tb(result.exc_info[2])) + ) + ) + pytest.fail("Project update call failed") + + LOG.info( + "Testing 'calm update project' command using cli switches for account addition" + ) + result = runner.invoke( + cli, + [ + "update", + "project", + self.dsl_project_name, + "--add_account", + AWS_ACCOUNT_NAME, + ], + ) + if result.exit_code: + cli_res_dict = {"Output": result.output, "Exception": str(result.exception)} + LOG.debug( + "Cli Response: {}".format( + json.dumps(cli_res_dict, indent=4, separators=(",", ": ")) + ) + ) + LOG.debug( + "Traceback: \n{}".format( + "".join(traceback.format_tb(result.exc_info[2])) + ) + ) + pytest.fail("Project update call failed") + + LOG.info("Success") + def _test_update_project_using_dsl_file(self): """ Removes user from given project. From e6cf34adae1cddcc007c55485080aa95da8908e4 Mon Sep 17 00:00:00 2001 From: abhijeetkaurav1st Date: Fri, 19 Nov 2021 23:16:12 +0530 Subject: [PATCH 3/4] Fixing project update using dsl file and adding test for it --- calm/dsl/cli/environments.py | 31 +++++--- calm/dsl/cli/projects.py | 72 ++++++++++++++++++ .../project/project_with_multi_env_update.py | 73 +++++++++++++++++++ tests/3_2_0/project/test_projects_env.py | 51 ++++++++++++- 4 files changed, 216 insertions(+), 11 deletions(-) create mode 100644 tests/3_2_0/project/project_with_multi_env_update.py diff --git a/calm/dsl/cli/environments.py b/calm/dsl/cli/environments.py index e3660994b..44dd303ee 100644 --- a/calm/dsl/cli/environments.py +++ b/calm/dsl/cli/environments.py @@ -152,16 +152,16 @@ def create_environment_from_dsl_class(env_cls, env_name="", metadata=dict()): return create_environment(env_payload) -def update_project_envs(project_name, env_uuids=[]): +def update_project_envs(project_name, remove_env_uuids=[], add_env_uuids=[]): """ Update project with the environment reference list if not present Args: project_name(str): Name of project - env_uuids(list): list of environment uuids + remove_env_uuids(list): list of env uuids to be removed from project + add_env_uuids(list): list of env uuid to be added in project Returns: None """ - - if not env_uuids: + if not (remove_env_uuids or add_env_uuids): return project_payload = get_project(project_name) @@ -170,11 +170,16 @@ def update_project_envs(project_name, env_uuids=[]): env_list = project_payload["spec"]["resources"].get( "environment_reference_list", [] ) - for _eu in env_uuids: + for _eu in add_env_uuids: env_list.append({"kind": "environment", "uuid": _eu}) + final_env_list = [] + for _edata in env_list: + if _edata["uuid"] not in remove_env_uuids: + final_env_list.append(_edata) + + project_payload["spec"]["resources"]["environment_reference_list"] = final_env_list project_uuid = project_payload["metadata"]["uuid"] - project_payload["spec"]["resources"]["environment_reference_list"] = env_list # TODO remove this infunction imports from .projects import update_project @@ -229,12 +234,13 @@ def create_environment_from_dsl_file(env_file, env_name, project_name): ContextObj.reset_configuration() LOG.info("Updating project for environment configuration") - update_project_envs(project_name, [env_std_out.get("uuid")]) + update_project_envs(project_name, add_env_uuids=[env_std_out.get("uuid")]) LOG.info("Project updated successfully") click.echo(json.dumps(env_std_out, indent=4, separators=(",", ": "))) - LOG.info("Updating environments cache ...") + LOG.info("Updating projects and environments cache ...") + Cache.sync_table(cache_type=CACHE.ENTITY.PROJECT) Cache.sync_table(cache_type=CACHE.ENTITY.ENVIRONMENT) LOG.info("[Done]") @@ -300,7 +306,8 @@ def update_environment_from_dsl_file(env_name, env_file, project_name): } click.echo(json.dumps(stdout_dict, indent=4, separators=(",", ": "))) - LOG.info("Updating environments cache ...") + LOG.info("Updating projects and environments cache ...") + Cache.sync_table(cache_type=CACHE.ENTITY.PROJECT) Cache.sync_table(cache_type=CACHE.ENTITY.ENVIRONMENT) LOG.info("[Done]") @@ -483,6 +490,10 @@ def delete_environment(environment_name, project_name): raise Exception("[{}] - {}".format(err["code"], err["error"])) LOG.info("Environment {} deleted".format(environment_name)) - LOG.info("Updating environments cache ...") + LOG.info("Updating project for environment configuration") + update_project_envs(project_name, remove_env_uuids=[environment_id]) + + LOG.info("Updating environments and projects cache ...") + Cache.sync_table(cache_type=CACHE.ENTITY.PROJECT) Cache.sync_table(cache_type=CACHE.ENTITY.ENVIRONMENT) LOG.info("[Done]") diff --git a/calm/dsl/cli/projects.py b/calm/dsl/cli/projects.py index a79d3eca7..d1a96fcbe 100644 --- a/calm/dsl/cli/projects.py +++ b/calm/dsl/cli/projects.py @@ -667,6 +667,78 @@ def update_project_from_dsl(project_name, project_file): "default_environment_reference" ] = default_env_ref + # Get the diff in subnet and account payload for project usage + existing_subnets = [ + _subnet["uuid"] + for _subnet in old_project_payload["spec"]["resources"].get( + "subnet_reference_list", [] + ) + ] + existing_subnets.extend( + [ + _subnet["uuid"] + for _subnet in old_project_payload["spec"]["resources"].get( + "external_network_list", [] + ) + ] + ) + + new_subnets = [ + _subnet["uuid"] + for _subnet in project_payload["spec"]["resources"].get( + "subnet_reference_list", [] + ) + ] + new_subnets.extend( + [ + _subnet["uuid"] + for _subnet in project_payload["spec"]["resources"].get( + "external_network_list", [] + ) + ] + ) + + existing_accounts = [ + _acc["uuid"] + for _acc in old_project_payload["spec"]["resources"].get( + "account_reference_list", [] + ) + ] + new_accounts = [ + _acc["uuid"] + for _acc in project_payload["spec"]["resources"].get( + "account_reference_list", [] + ) + ] + + project_usage_payload = { + "filter": { + "subnet_reference_list": list(set(existing_subnets) - set(new_subnets)), + "account_reference_list": list(set(existing_accounts) - set(new_accounts)), + } + } + + LOG.info("Checking project usage") + res, err = client.project.usage(project_uuid, project_usage_payload) + if err: + LOG.error(err) + sys.exit(-1) + + project_usage = res.json() + msg_list = [] + should_update_project = is_project_updation_allowed(project_usage, msg_list) + if not should_update_project: + LOG.error("Project updation failed") + click.echo("\n".join(msg_list)) + click.echo( + json.dumps( + project_usage["status"].get("resources", {}), + indent=4, + separators=(",", ": "), + ) + ) + sys.exit(-1) + # Setting correct metadata for update call project_payload["metadata"] = old_project_payload["metadata"] diff --git a/tests/3_2_0/project/project_with_multi_env_update.py b/tests/3_2_0/project/project_with_multi_env_update.py new file mode 100644 index 000000000..7cc583797 --- /dev/null +++ b/tests/3_2_0/project/project_with_multi_env_update.py @@ -0,0 +1,73 @@ +# Note: In given example, we not added environment reference anywhere. +# Project create command will pick one Environment module from file and attaches to project + +import json + +from calm.dsl.builtins import Project, read_local_file +from calm.dsl.builtins import Provider, Ref + + +DSL_CONFIG = json.loads(read_local_file(".tests/config.json")) +CENTOS_CI = DSL_CONFIG["AHV"]["IMAGES"]["DISK"]["CENTOS_7_CLOUD_INIT"] +SQL_SERVER_IMAGE = DSL_CONFIG["AHV"]["IMAGES"]["CD_ROM"]["SQL_SERVER_2014_x64"] + +# Accounts +ACCOUNTS = DSL_CONFIG["ACCOUNTS"] + +NTNX_ACCOUNT_1 = ACCOUNTS["NUTANIX_PC"][0] +NTNX_ACCOUNT_1_NAME = NTNX_ACCOUNT_1["NAME"] +NTNX_ACCOUNT_1_SUBNET_1 = NTNX_ACCOUNT_1["SUBNETS"][0]["NAME"] +NTNX_ACCOUNT_1_SUBNET_1_CLUSTER = NTNX_ACCOUNT_1["SUBNETS"][0]["CLUSTER"] +NTNX_ACCOUNT_1_SUBNET_2 = NTNX_ACCOUNT_1["SUBNETS"][1]["NAME"] +NTNX_ACCOUNT_1_SUBNET_2_CLUSTER = NTNX_ACCOUNT_1["SUBNETS"][1]["CLUSTER"] + +AWS_ACCOUNT = ACCOUNTS["AWS"][0] +AWS_ACCOUNT_NAME = AWS_ACCOUNT["NAME"] + +AZURE_ACCOUNT = ACCOUNTS["AZURE"][0] +AZURE_ACCOUNT_NAME = AZURE_ACCOUNT["NAME"] + +GCP_ACCOUNT = ACCOUNTS["GCP"][0] +GCP_ACCOUNT_NAME = GCP_ACCOUNT["NAME"] + +VMWARE_ACCOUNT = ACCOUNTS["VMWARE"][0] +VMWARE_ACCOUNT_NAME = VMWARE_ACCOUNT["NAME"] + +K8S_ACCOUNT = ACCOUNTS["K8S"][0] +K8S_ACCOUNT_NAME = K8S_ACCOUNT["NAME"] + +USER = DSL_CONFIG["USERS"][0] +USER_NAME = USER["NAME"] + + +class SampleDslProject(Project): + """Sample DSL Project with environments""" + + providers = [ + Provider.Ntnx( + account=Ref.Account(NTNX_ACCOUNT_1_NAME), + subnets=[ + Ref.Subnet( + name=NTNX_ACCOUNT_1_SUBNET_2, + cluster=NTNX_ACCOUNT_1_SUBNET_2_CLUSTER, + ), + Ref.Subnet( + name=NTNX_ACCOUNT_1_SUBNET_1, + cluster=NTNX_ACCOUNT_1_SUBNET_1_CLUSTER, + ), + ], + ), + Provider.Gcp(account=Ref.Account(GCP_ACCOUNT_NAME)), + Provider.Vmware(account=Ref.Account(VMWARE_ACCOUNT_NAME)), + Provider.K8s(account=Ref.Account(K8S_ACCOUNT_NAME)), + Provider.Aws(account=Ref.Account(AWS_ACCOUNT_NAME)), + Provider.Azure(account=Ref.Account(AZURE_ACCOUNT_NAME)), + ] + + users = [Ref.User(USER_NAME)] + + quotas = { + "vcpus": 1, + "storage": 2, + "memory": 1, + } diff --git a/tests/3_2_0/project/test_projects_env.py b/tests/3_2_0/project/test_projects_env.py index 88398cda2..4731e14bd 100644 --- a/tests/3_2_0/project/test_projects_env.py +++ b/tests/3_2_0/project/test_projects_env.py @@ -18,6 +18,7 @@ LOG = get_logging_handle(__name__) DSL_PROJECT_PATH = "tests/3_2_0/project/project_with_multi_env.py" +DSL_UPDATE_PROJECT_PATH = "tests/3_2_0/project/project_with_multi_env_update.py" DSL_ENVIRONMENT_PATH = "tests/3_2_0/project/sample_environment.py" ENV_1_NAME = "ProjEnvironment1" ENV_2_NAME = "ProjEnvironment2" @@ -200,8 +201,56 @@ def test_create_project(self): self._test_delete_environment() + self._test_env_removal_on_projects_update_dsl() + self._test_delete_project() + def _test_env_removal_on_projects_update_dsl(self): + """tests whether updating project through dsl will not delete existing environment""" + + runner = CliRunner() + project_data = get_project(self.project_name) + existing_env_uuids = [ + _env["uuid"] + for _env in project_data["spec"]["resources"]["environment_reference_list"] + ] + + LOG.info("Updating Project using file at {}".format(DSL_UPDATE_PROJECT_PATH)) + result = runner.invoke( + cli, + [ + "update", + "project", + self.project_name, + "--file={}".format(DSL_UPDATE_PROJECT_PATH), + ], + ) + + if result.exit_code: + cli_res_dict = {"Output": result.output, "Exception": str(result.exception)} + LOG.debug( + "Cli Response: {}".format( + json.dumps(cli_res_dict, indent=4, separators=(",", ": ")) + ) + ) + LOG.debug( + "Traceback: \n{}".format( + "".join(traceback.format_tb(result.exc_info[2])) + ) + ) + pytest.fail("Project update command failed") + + LOG.info("Checking presence of environments in project") + project_data = get_project(self.project_name) + project_str = json.dumps(project_data) + new_env_uuids = [ + _env["uuid"] + for _env in project_data["spec"]["resources"]["environment_reference_list"] + ] + assert (env_uuid in new_env_uuids for env_uuid in existing_env_uuids) + assert NTNX_ACCOUNT_2_UUID not in project_str + assert NTNX_ACCOUNT_2_SUBNET_1_UUID not in project_str + def _test_env_data(self): """tests env data i.e. accounts, subnets in project-environments""" @@ -290,7 +339,7 @@ def _test_delete_project(self): # so assigning it to true before starting deletion of project self.project_deleted = True - LOG.info("Deleting Project using file at {}".format(DSL_PROJECT_PATH)) + LOG.info("Deleting Project {}".format(self.project_name)) result = runner.invoke(cli, ["delete", "project", self.project_name]) if result.exit_code: From ead214b2fcfabbe82dd435b8ebe3564adc8fa171 Mon Sep 17 00:00:00 2001 From: abhijeetkaurav1st Date: Mon, 22 Nov 2021 09:48:52 +0530 Subject: [PATCH 4/4] Added changes in readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d4efa49d4..a3dc8feed 100644 --- a/README.md +++ b/README.md @@ -139,8 +139,8 @@ Use `calm get roles` to list all roles in PC. The below roles are relevant for C - Create project on Calm Server: `calm create project --file --name --description `. - List projects: `calm get projects`. Get projects, optionally filtered by a string - Describe project: `calm describe project `. It will print summary of project. -- Update project using dsl file: `calm update project --file `. -- Update project using cli switches: `calm update project --add_user/--remove_user --add_group/--remove_group `. +- Update project using dsl file: `calm update project --file `. Environments will not be updated as part of this operation. +- Update project using cli switches: `calm update project --add_user/--remove_user --add_group/--remove_group --add_account/--remove_account `. - Delete project: `calm delete project `. ### Environments