Skip to content

Conversation

@yugangw-msft
Copy link
Contributor

This is a prototype, but sufficient for review to get the ball rolling.
Per conversation with @johanste, @derekbekoe, the mechanisms will be simple. A few notes:

  1. The configured defaults will persist in ~/.azure/config, or you can use environment variables
  2. We will only apply the defaults on an required argument, not for optional arg. This way vm list/'appservice web list' will still work to list across the subscription
  3. If you are using --ids, I will still respect it, but I made a change of not to emit a warning if the id conflicts with the default resource group.

For reference, the command flow will look like

az group create -n yugangw3 -l eastus
az appservice set-defaults --resource-group yugangw3
az appservice plan create -n yugangw3-plan --sku s1
az appservice plan show -n yugangw3-plan
az appservice web create --plan yugangw3-plan -n yugangw3-web
az appservice set-defaults --webapp yugangw3-web
az appservice web show

@yugangw-msft
Copy link
Contributor Author

This should fix #1377. With that, IMO, we can live with #1771

@yugangw-msft yugangw-msft changed the title core: support setting default values for common argument like default resource group, default web core: support setting default values for common arguments like default resource group, default web, default vm Mar 8, 2017
@codecov-io
Copy link

codecov-io commented Mar 8, 2017

Codecov Report

Merging #2414 into master will decrease coverage by 0.03%.
The diff coverage is 60%.

@@            Coverage Diff             @@
##           master    #2414      +/-   ##
==========================================
- Coverage   72.47%   72.44%   -0.04%     
==========================================
  Files         361      362       +1     
  Lines       19509    19547      +38     
  Branches     2856     2865       +9     
==========================================
+ Hits        14139    14160      +21     
- Misses       4463     4473      +10     
- Partials      907      914       +7
Impacted Files Coverage Δ
...ure-cli-core/azure/cli/core/commands/parameters.py 70.58% <ø> (ø)
src/azure-cli-core/azure/cli/core/commands/arm.py 88.83% <0%> (-0.22%)
...urce/azure/cli/command_modules/resource/_params.py 100% <100%> (ø)
...ource/azure/cli/command_modules/resource/custom.py 57.18% <100%> (-0.31%)
...ure/azure/cli/command_modules/configure/_params.py 100% <100%> (ø)
...ure-cli-vm/azure/cli/command_modules/vm/_params.py 96.01% <100%> (ø)
...ce/azure/cli/command_modules/appservice/_params.py 93.93% <100%> (+0.06%)
src/azure-cli-core/azure/cli/core/_config.py 96.42% <100%> (+0.06%)
...re/azure/cli/command_modules/configure/init.py 100% <100%> (ø)
...gure/azure/cli/command_modules/configure/custom.py 16.21% <23.07%> (-0.79%)
... and 8 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9abbb0e...aede985. Read the comment docs.

@lostintangent
Copy link
Member

This is really awesome to see this experience moving forward! I just had a few thoughts on this specific proposal:

  1. Is the "default resource group" mechanism specific to App Service? It seems odd that this can only be set via a command within the appservice group, but I may be missing something. It would presumably be helpful to be able to define the RG you're working with, and have that affect your holistic CLI experience, regardless what compute/service type I'm currently using.

  2. Could the location also be a global setting that could be configured? That way, you wouldn't need to specify it when creating a new resource group?

  3. I wonder whether it would make sense for certain defaults be saved along with a project, as opposed to being a global setting (e.g. the web abb and plan). This proposal is a great step forward, but I can't help but think that it would be even more natural if the az appservice web commands (for example) would pick up defaults based on some file that is stored in my CWD (a non-user global .azure file?). That way, a dev could clone a project and immediately work with the CLI without needing to call set-defaults manually. It would also allow users to CD into different directories on the same machine and have it do the right thing. Maybe it would even be possible to have a config resolution hierarchy, such that you could have a per-project .azure file, as well as a global one, and the former would be given precedence over the later. This would be pretty awesome, and the user could choose to check-in their project-specific .azure file if they want that to persist. This solution would also simply augment this proposal, with the global solution existing in addition to it.

  4. I'd love to see certain defaults set automatically for you, so that you don't need to call set-defaults explicitly if we can layer on some workflow opinion. For example, if you create an App Service plan and/or web app, why can't we immediately set those as the defaults on your behalf? As long as it was clear that this behavior was occurring, you could easily revert it (calling set-defaults again), etc. it seems like it would lead to a much smoother/declarative experience.

  5. I still believe very strongly about eliminating the need to explicitly create a resource group and app service plan when creating a web app. These concepts just represent friction for new users trying to get started with Azure. In addition, once we auto-create resources, we could choose to then do Initial code to sign user in and wire up the credentials in a sample storage command #3 and set those auto-generated resources as the default for you

Suggestion #3 and #4 would effectively allow translating your sample code:

az group create -n yugangw3 -l eastus
az appservice set-defaults --resource-group yugangw3
az appservice plan create -n yugangw3-plan --sku s1
az appservice plan show -n yugangw3-plan
az appservice web create --plan yugangw3-plan -n yugangw3-web
az appservice set-defaults --webapp yugangw3-web
az appservice web show

Into the following (note the need to configure the SKU and location as part of the web app, since you're not referencing an existing plan/group):

$ az appservice web create -n yugangw3-web -s s1 -l eastus
az appservice web show

Most of this could presumably be layered on top of the work you're enabling in this proposal, but it would be great to determine what degree of opinion you feel is appropriate for Az CLI, as opposed to a derivative CLI that could choose to be more prescriptive.

@yugangw-msft
Copy link
Contributor Author

@lostintangent. Thanks for the suggestion.
For 1, @derekbekoe asked about the same thing. We chatted and I will remove the strong type set-defaults under individual module, rather just expand the az configure command. To have the e2e closed, I will update the argument help of webapp to point to that command
For 2, I will add location. At this point, this PR focuses more on the infrastructure level change so to expose the general support
For 3, This is about the cli configuration management, related but a different thing.
For 4, Good point, but let us track it separately, particular per user voices. The global configuration, due to the nature and impact, is confusion prone. For now I like to keep it as explicit as possible, so you need to opt-in. Once it gets stabilized, I can consider to throwing in more hidden logics.
For 5, yep we have a separate issue on it already

@lostintangent
Copy link
Member

@yugangw-msft Makes sense. I recognize there are issues for all of these items, I just wanted to mention them since you mentioned not believing that #1771 would be necessary, and I wanted to provide my opinion about that and surrounding features for simplifying this E2E. Thanks!

@lostintangent
Copy link
Member

@yugangw-msft How will this look now to set the default RG via the az configure command? az configure -g foo? Also, what does it look like to set a default web app/etc. via az configure?

Will this be a way to discover the set of known name/value pairs that can be defaulted via the az configure command?

@yugangw-msft
Copy link
Contributor Author

yugangw-msft commented Mar 9, 2017

How will this look now to set the default RG via the az configure command

az configure --default-resource-group my RG

what does it look like to set a default web app/etc. via az configure

az configure --section appservice --name default_webapp --value myweb1

Will this be a way to discover the set of known name/value pairs that can be defaulted via the az configure command?

The discoverability was the main reason I exposed az appservice set-default. With it is gone, I would let the webapp name argument's help to fill in the gap, ~~~which is auto-gen'd for any such configurable arguments~~~ (no, the command author need to edit the help to call it out)

(env) D:\sdk\azure-cli>az appservice web show -h

Command
    az appservice web show: Show a web app.

Arguments
    --slot -s          : The name of the slot. Default to the productions slot if not specified.

Resource Id Arguments
    --ids              : One or more resource IDs (space delimited). If provided, no other 'Resource
                         Id' arguments should be specified.
    --name -n          : Name of the web. You can configure the default web using 'az configure
                         --section appservice --name default_webapp'.

@lostintangent
Copy link
Member

Great, thanks! The default_webapp value "feels" a little strange for some reason. But that's probably just me. I wonder whether we couldn't position the configure-time settings as always being about default values, and therefore, the individual value names didn't need to explicitly call that out.

Just thinking out loud, the following workflow feels simple and natural to me (collapsing the section/name into a single slashed value, removing the default_ prefix, and adding shorthand aliases):

$ az configure --setting appservice/webapp --value myweb1

# With shorthand aliases
$ az configure -s appservice/webapp -v webapp1

Either way, this is an awesome PR. I'm just thinking out loud since you mentioned this was a prototype for discussion :)

@derekbekoe
Copy link
Member

$ az configure --setting appservice/webapp --value myweb1

@lostintangent I like this suggestion.

Behind the scenes, it gets saved in the config like this which makes sense to me:

[appservice]
webapp = myweb1

I came up with the idea of az configure --section ... --name ... --value ... but like this more.

@derekbekoe
Copy link
Member

@yugangw-msft Are we making a special case expection for resource group having it's own param name like this?

az configure --default-resource-group ...

What if we have az configure --setting resource-group --value MyRG.
Since there's no 'namespace' (i.e. NAMESPACE/KEY) in the setting param, it will go in core like so:

[core]
resource-group = MyRG

@lostintangent
Copy link
Member

@derekbekoe I like that RG suggestion!

- name: configure a default webapp
text: >
az configure --section appservice --name default_webapp_name --value myweb1
az configure --section compute/vm --value myvm
Copy link
Member

Choose a reason for hiding this comment

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

Should this be "--setting"?

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 catch.

configure common settings
:param str section: configuration section
:param str name: configuration variable name
:param str setting: configuration variable, e.g. resource-group, appservice/webapp, compute/vm
Copy link
Member

Choose a reason for hiding this comment

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

The example here uses "resource-group", whereas it looks like it's actually "resource_group". I actually prefer the hyphenated version, since that seems to be consistent with what the CLI currently uses for word separation.

Copy link
Member

Choose a reason for hiding this comment

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

Actually, in order to match the name of the existing noun in the CLI, could we just call this setting "group"?

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 initially used resource-group, but switched to resource_group, as the existing variables there all use _ style. I would leave to @derekbekoe for final comment.
Regarding to group, I am fine with that with the only concern that by appearing in the global config, the meaning is not easy to grab, as the group could mean lots of other things

Copy link
Contributor Author

@yugangw-msft yugangw-msft Mar 9, 2017

Choose a reason for hiding this comment

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

Since we are here, should we support to set multiple values at the same time? Say use a space separated name=value pairs
--settings resource_group=rg1 appservice/web=myweb compute/vm=myvm1

Copy link
Member

Choose a reason for hiding this comment

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

I was literally just thinking the same thing! :) Yeah this would be great, so that you could easily set the group and VM/web app at the same time.

Copy link
Member

Choose a reason for hiding this comment

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

The actual setting saved to config should be underscore cased but the command could accept both underscore and dashed forms?


register_cli_argument('appservice web', 'slot', options_list=('--slot', '-s'), help="the name of the slot. Default to the productions slot if not specified")
register_cli_argument('appservice web', 'name', configured_default='appservice/default_webapp_name',
register_cli_argument('appservice web', 'name', configured_default='appservice/webapp',
Copy link
Member

Choose a reason for hiding this comment

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

What do you think about calling this "appservice/web" in order to match the noun already used in the CLI? I like the consistency, and it feels a little more intuitive.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed.

az configure --setting resource_group myRG
- name: configure a default webapp
text: >
az configure --section compute/vm --value myvm
Copy link
Member

Choose a reason for hiding this comment

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

For the same reason I mentioned for group and appservice/web, I'd love to discuss whether this could just be "vm" instead of "compute/vm", since the former would match the "resource hierarchy" of the respective noun in the CLI.

From a discoverability perspective, having the setting names match their corresponding command group structure seems like it could help make them more predictable.

Copy link
Member

Choose a reason for hiding this comment

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

Having just 'vm' would mean it gets put in 'core':

[core]
vm = myvm

IMO that doesn't look quite right as it shouldn't be under core.

Suggestion, 'vm/name':

[vm]
name = myvm

export AZURE_VM_NAME=myvm

Copy link
Contributor Author

@yugangw-msft yugangw-msft Mar 9, 2017

Choose a reason for hiding this comment

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

I thought about that yesterday as well.
For this feature, there is no good discoverability, because the whole process is opt-in, so you will need to check out the help on the argument to know whether this arg's default value can be configured. That is why I initially exposed a strong typed command of set-defaults
That said, I still think we can do some change to get closer to simplicity. Right now 'resource_group' goes to [core] section with other configs, and "appservice/webapp" goes to [appservice] section, I am thinking maybe we can just use a section like [default], and put in all those values there? This way, it is fine to just use group. The configure command can be simplified az configure --settings vm=vm1 group=myrg web=myweb.

register_cli_argument('appservice', 'resource_group_name', arg_type=resource_group_name_type)
register_cli_argument('appservice', 'location', arg_type=location_type)

register_cli_argument('appservice set-defaults', 'clear_all', action='store_true')
Copy link
Member

Choose a reason for hiding this comment

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

We don't need this anymore right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

right.

register_cli_argument('appservice web create', 'name', options_list=('--name', '-n'), help='name of the new webapp')
register_cli_argument('appservice web', 'name', configured_default='appservice/webapp',
arg_type=name_arg_type, completer=get_resource_name_completion_list('Microsoft.Web/sites'), id_part='name',
help="name of the web. You can configure the default web using 'az configure --section appservice --name default_webapp_name'")
Copy link
Member

Choose a reason for hiding this comment

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

I believe the help is incorrect now the configure command has changed.

examples:
- name: configure a default resource group
text: >
az configure --setting resource_group myRG
Copy link
Member

Choose a reason for hiding this comment

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

IMO in the help, this should be resource-group instead of resource_group.
But in the actual config file, it should be saved in underscore casing.
So in your logic you can do str.replace('-', '_'). Then you could actually support both - and _ with little effort.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. Like i said, I am thinking we just use a dedicated section [default], and use group

Copy link
Contributor Author

@yugangw-msft yugangw-msft Mar 10, 2017

Choose a reason for hiding this comment

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

Per our conversation, let us not do the character replacement which will cause inconsistency when users display existing configurations. So I will do 2 things

  • Let go with group
  • We will try to align with the top categories, which will give us vm/name, vmss/name, appservice/webname.

az configure --setting resource_group myRG
- name: configure a default webapp
text: >
az configure --section compute/vm --value myvm
Copy link
Member

Choose a reason for hiding this comment

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

Having just 'vm' would mean it gets put in 'core':

[core]
vm = myvm

IMO that doesn't look quite right as it shouldn't be under core.

Suggestion, 'vm/name':

[vm]
name = myvm

export AZURE_VM_NAME=myvm

setattr(arg.type, 'configured_default_applied', True)
parts = def_config.split('/')
section = 'core' if len(parts) == 1 else parts[0]
config_name = parts[-1]
Copy link
Member

Choose a reason for hiding this comment

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

The 3 lines above should go into _config.py in a helper method.
In the PR, similar logic is used in configure/custom.py.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will refactor

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


return

# if nothing supplifed, we go interactively
Copy link
Member

Choose a reason for hiding this comment

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

Typo supplifed.
Not sure if you wanted supplied or specified 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Guess both crossed my mind at the same time. Thanks.

:param str value: configuration variable value
'''
if setting or value:
if bool(setting) != bool(value):
Copy link
Member

Choose a reason for hiding this comment

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

What's this for?

Copy link
Contributor Author

@yugangw-msft yugangw-msft Mar 9, 2017

Choose a reason for hiding this comment

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

supposed to verify both values are true :). Let me get it clearer by using if not setting or not value:


def _normalize_config_value(value):
if value:
value = '' if value in ["''", '""'] else value
Copy link
Member

Choose a reason for hiding this comment

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

Is this to remove the default?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

correct

register_cli_argument('group export', 'include_comments', action='store_true')
register_cli_argument('group export', 'include_parameter_default_value', action='store_true')
register_cli_argument('group create', 'resource_group_name', completer=None)
register_cli_argument('group create', 'rg_name', options_list=('--name', '-n'), help='name of the new resource group', completer=None)
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't use resource_group_name_type in parameters.py.
Does that mean the default RG won't apply here?

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, for create, we should not use the default RG

Copy link
Contributor Author

Choose a reason for hiding this comment

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

FYI, for other renaming on vm/vmss/vmss, my recent push has got rid of it. I decided to handle them in one place, that for create command, the name part will not load the default configured value. This is not a new thing, we did the same on splitting id-part

# VM CREATE PARAMETER CONFIGURATION

register_cli_argument('vm create', 'name', name_arg_type, validator=_resource_not_exists('Microsoft.Compute/virtualMachines'))
register_cli_argument('vm create', 'new_vm_name', options_list=('--name', '-n'), metavar='NAME',
Copy link
Member

Choose a reason for hiding this comment

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

Q: Why did you need to change to new_vm_name?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So that this arg name would not pick the default from config, and we only need to do it for create, It appears the arg matching we have is based on string match. I can special case in the core layer to skip any create command like we did in other places, but again since it is limited on create command, so I do it at command module layer

@lostintangent
Copy link
Member

lostintangent commented Mar 10, 2017

@yugangw-msft @derekbekoe Personally, I'm not entirely in love with the new name suffixing on the settings, and it seems like maybe this was added to avoid keys which are too generic? For the sake of discussion, I'll just throw out one last thought and then move along :)

What if we renamed the --settings flag on the configure command to --defaults (after looking at so many examples, I think this may be clearer), and you persisted the specified "section names" into a section in the config file called "defaults", as opposed to "core"? That way, you didn't need to worry about "vm" being too generic in the underlying config file, because it would be a key in the "defaults" section, along with "group", and "location", etc. Whereas, "appservice/web" would be a "web" key, within an "appservice" section, that was embedded within the "defaults" section. This might also make the contents of the config file itself read more clearly, while maintaining a clean CLI interface as well:

[core]
foo = bar

[defaults]
group = MyRG
vm = MyVM

[defaults.appservice]
web = MyWeb

Maybe it's just me, but I really like the idea of the "key path" matching the same hierarchy as the CLI itself. It just feels very clean. With this change, the use of the command also "reads" pretty naturally IMO (E.g. "Configure the default group to be 'foo' and the default vm to be 'bar'").

az configure --defaults group=foo vm=bar
az configure --defaults appservice/web=baz

@yugangw-msft
Copy link
Contributor Author

yugangw-msft commented Mar 10, 2017

@lostintangent,
--defaults looks good to me. The [defaults] section also looks good to me. I mentioned similar idea early on.
I do suggest we put all values in one section, not multiple. The naming can be suffixed to differentiate let us say, appservice_web. To have multiple sections is overly engineering to me. Ultimately, very limited arguments will be configured with default, and get communicated through arg's help. User can't really discover too much by themselves.

@lostintangent
Copy link
Member

@yugangw-msft Sounds good. I was by no means partial to the sub-section vs. key prefixing solution, I was just throwing out one approach, that may or may be simpler based on how you're actually persisting/loading the config data.

Thanks so much for having this awesome open discussion!

@lostintangent
Copy link
Member

LGTM!

Copy link
Member

@derekbekoe derekbekoe left a comment

Choose a reason for hiding this comment

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

LGTM! Added one more q.

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.

6 participants