Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Core] enable local context for global resource group #12741

Merged
merged 11 commits into from
Mar 26, 2020
Merged

[Core] enable local context for global resource group #12741

merged 11 commits into from
Mar 26, 2020

Conversation

arrownj
Copy link
Contributor

@arrownj arrownj commented Mar 25, 2020

This is the first version for local context.

You can find the very early spec here.

There are also some other useful linkes:

And we have come with an agreement with PM that we will divide these works into several phases.

In this sprint, we add two new commands:

az local-context on

az local-context off

and only enable the global parameter --resource-group-name.

After this change, if user run a command and specify --resource-group-name for it, the value will automatically saved and will be used in following commands if the local-context is turned on.

Description of PR (Mandatory)
(Why this PR? What is changed? What is the effect? etc. A high-quality description can accelerate the review process)

Testing Guide
(Example commands with explanations)

History Notes:
(Fill in the following template if multiple notes are needed, otherwise PR title will be used for history note.)

[Component Name 1] (BREAKING CHANGE:) (az command:) make some customer-facing change.
[Component Name 2] (BREAKING CHANGE:) (az command:) make some customer-facing change.


This checklist is used to make sure that common guidelines for a pull request are followed.

@yonzhan
Copy link
Collaborator

yonzhan commented Mar 25, 2020

local context

@jiasli
Copy link
Member

jiasli commented Mar 25, 2020

It'll be great if the PR description can have some brief introduction about the implementation:

  • How is it achieved?
  • Where is the context stored on the file system?
  • How to use it grammatically?

The design spec is user-oriented. We'd better also have some developer-orientated doc.

dir_path = os.path.join(current_dir, dir_name)
file_path = os.path.join(dir_path, file_name)
if os.path.isfile(file_path):
self._file_chain.append(_ConfigFile(dir_path, file_path))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add try/except

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use os.access(path, mode) to check whether have R/W permission.

Comment on lines 299 to 300
help='name of the new resource group', completer=None, lc_actions=[STORE], lc_scopes=[GLOBAL],
lc_name='resource_group_name')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's extract lc_actions, lc_scopes and lc_name to a LocalContext class, like deprecate_info

g.command('import-method', 'import_data', deprecate_info=g.deprecate(redirect='redis import', hide=True))

https://github.com/microsoft/knack/blob/5f0bcc2ae416c8f2834db71b49040976e1d7a29d/knack/commands.py#L377-L379

    def deprecate(self, **kwargs):
        kwargs['object_type'] = 'command'
        return Deprecated(self.command_loader.cli_ctx, **kwargs)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used a LocalContextAttribute class and it is a little bit different with Deprecated. Please take a look at it.

Comment on lines +15 to +17
STORE = 'STORE' # action for a parameter in local context, STORE means its value will be saved to local context
USE = 'USE' # action for a parameter in local context, USE means will read value from local context for this parameter
ALL = 'ALL' # effective level of local context, ALL means all commands can share this parameter value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
STORE = 'STORE' # action for a parameter in local context, STORE means its value will be saved to local context
USE = 'USE' # action for a parameter in local context, USE means will read value from local context for this parameter
ALL = 'ALL' # effective level of local context, ALL means all commands can share this parameter value
STORE = 'STORE' # action for a parameter in local context. STORE means its value will be saved to local context
USE = 'USE' # action for a parameter in local context. USE means the value of this parameter will be read from local context
ALL = 'ALL' # effective level of local context. ALL means all commands can share this parameter value

Comment on lines +112 to +122
""" Local Context Attribute arguments

Save argument value to local context if it is defined as STORE and user specify a value for it.

:param parsed_args: Parsed args which return by AzCliCommandParser parse_args
:type parsed_args: Namespace
:param argument_definitions: All available argument definitions
:type argument_definitions: dict
:param specified_arguments: Arguments which user specify in this command
:type specified_arguments: list
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
""" Local Context Attribute arguments
Save argument value to local context if it is defined as STORE and user specify a value for it.
:param parsed_args: Parsed args which return by AzCliCommandParser parse_args
:type parsed_args: Namespace
:param argument_definitions: All available argument definitions
:type argument_definitions: dict
:param specified_arguments: Arguments which user specify in this command
:type specified_arguments: list
"""
""" Local Context Attribute arguments
Save argument value to local context if it is defined as STORE and user specifies a value for it.
:param parsed_args: Parsed args returned by AzCliCommandParser parse_args
:type parsed_args: Namespace
:param argument_definitions: All available argument definitions
:type argument_definitions: dict
:param specified_arguments: Arguments which user specifies in this command
:type specified_arguments: list
"""

@@ -9,7 +9,7 @@

CLI_COMMON_KWARGS = ['min_api', 'max_api', 'resource_type', 'operation_group',
'custom_command_type', 'command_type', 'is_preview', 'preview_info',
'is_experimental', 'experimental_info']
'is_experimental', 'experimental_info', 'local_context_attribute']
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as we synced offline, is it really necessary to add the attribute chaneges in this release? this release suppose be a light change only for resource group by extending existing az config.

self._load_file_chain()
except Exception: # pylint: disable=broad-except
raise CLIError('fail to turn on local context in {}.'.format(os.path.dirname(current_dir)))
logger.warning('local context in current directory is turned on.')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consistent message, either enable or turned on

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

if value:
logger.debug("local context '%s' for arg %s", value, arg.name)
overrides.settings['default'] = DefaultStr(value)
overrides.settings['required'] = False
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overrides.settings['required'] = False [](start = 20, length = 38)

will this statement "overrides.settings['required'] = False" impact the ux like -h ?

Copy link
Contributor Author

@arrownj arrownj Mar 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Yes, if the value is saved in local context, it will show the value as default in -h. I think we need add more information for this, such as from local context.
And if local context is not turned on, -h will behavior as before.

def _get_values(self, action, arg_strings):
value = super(AzCliCommandParser, self)._get_values(action, arg_strings)
if action.dest and isinstance(action.dest, str) and not action.dest.startswith('_'):
self.specified_arguments.append(action.dest)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks tricky to add this logic in a _get_value method

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a function which is called in the ArgumentParser.parse_args(). The input for parse_args() is the command string(such as vm create -g rg -n vm_name), and _get_values() is called per parameter. For the given example, it will call _get_values for -g and -n. And here we want to collect the user specified arguments, I think it is a good place to add logic here.

# Stop if already in root drive
if current_dir == os.path.dirname(current_dir):
break
current_dir = os.path.dirname(current_dir)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if cwd is d:\c1, you can only have two sessions to use context file? I think it is not a good idea to add new file in different folder levels. can you use different file name if the file is locked by other session?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add debug log if _load_local_context_file fail


In reply to: 398340365 [](ancestors = 398340365)

raise CLIError('fail to turn on local context in {}.'.format(current_dir))
logger.warning('local context in %s is turned on.', current_dir)
else:
raise CLIError('local context is already turned on in {}'.format(current_dir))
Copy link
Member

@qianwens qianwens Mar 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

raise CLIError [](start = 12, length = 14)

I think it is better to just logger.warning here, no need to raise error if it is already on

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good suggestion.

break # load only one local context
# Stop if already in root drive
if current_dir == os.path.dirname(current_dir):
break
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

break [](start = 16, length = 5)

raise error when turn_on ?

Copy link
Member

@qianwens qianwens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

Copy link
Contributor

@haroldrandom haroldrandom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, learnt a lot.

Comment on lines +23 to +24
g.command('on', 'turn_local_context_on')
g.command('off', 'turn_local_context_off')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The command name comes from PM? According to command name convention, the last word should be a verb. I'm personally fine with the on/off, not sure if we have simliar existing commands.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, PM prefer such commands ...

# Stop if already in root drive
if current_dir == os.path.dirname(current_dir):
if self._local_context_file is None:
logger.debug('local context is not turned on in %s and all its parent directories', current_dir)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

current_dir is now root, should it be the original directory get by os.getcwd()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just debug information, for simplicity, let's just keep this message.

@@ -8,6 +8,7 @@
import unittest

from azure.cli.testsdk import ScenarioTest
from knack.util import CLIError
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this unused import.

@arrownj arrownj merged commit 34f9033 into Azure:dev Mar 26, 2020
@arrownj arrownj deleted the localcontext3 branch July 29, 2020 07:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants