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} Move clouds.config to config dir #26138

Draft
wants to merge 4 commits into
base: dev
Choose a base branch
from

Conversation

bebound
Copy link
Contributor

@bebound bebound commented Apr 13, 2023

Description

Related PR: #25689

At present, CLOUD_CONFIG_FILE is always ~/.azure/clouds.config.

TestProfile consists of different login test. When run az login, cloud config file is updated by login->_set_subscriptions -> set_cloud_subscription.

def set_cloud_subscription(cli_ctx, cloud_name, subscription):
if not _get_cloud(cli_ctx, cloud_name):
raise CloudNotRegisteredException(cloud_name)
config = configparser.ConfigParser()
config.read(get_cloud_config_file(cli_ctx))
if subscription:
try:
config.add_section(cloud_name)
except configparser.DuplicateSectionError:
pass
config.set(cloud_name, 'subscription', subscription)
else:
try:
config.remove_option(cloud_name, 'subscription')
except configparser.NoSectionError:
pass
with open(get_cloud_config_file(cli_ctx), 'w') as configfile:
config.write(configfile)

Simultaneous writing may lead to file interruption.

This PR fixes this bug by enabling random_config_dir in these test and move cloud config to cli_ctx.config.config_dir.

2023-04-13T04:49:15.7092388Z [gw0] [ 78%] FAILED tests/test_profile.py::TestProfile::test_refresh_accounts_one_user_account 
2023-04-13T04:49:15.7092660Z 
2023-04-13T04:49:15.7093062Z =================================== FAILURES ===================================
2023-04-13T04:49:15.7093394Z ______________ TestProfile.test_refresh_accounts_one_user_account ______________
2023-04-13T04:49:15.7093901Z [gw0] linux -- Python 3.10.10 /opt/az/bin/python3
2023-04-13T04:49:15.7094189Z self = <core.tests.test_profile.TestProfile testMethod=test_refresh_accounts_one_user_account>
2023-04-13T04:49:15.7094508Z get_user_credential_mock = <function get_user_credential at 0xffff9d0968c0>
2023-04-13T04:49:15.7094818Z create_subscription_client_mock = <function _create_subscription_client at 0xffff9d0944c0>
2023-04-13T04:49:15.7094960Z 
2023-04-13T04:49:15.7095334Z     @mock.patch('azure.cli.core._profile.SubscriptionFinder._create_subscription_client', autospec=True)
2023-04-13T04:49:15.7095771Z     @mock.patch('azure.cli.core.auth.identity.Identity.get_user_credential', autospec=True)
2023-04-13T04:49:15.7096119Z     def test_refresh_accounts_one_user_account(self, get_user_credential_mock, create_subscription_client_mock):
2023-04-13T04:49:15.7096401Z         mock_arm_client = mock.MagicMock()
2023-04-13T04:49:15.7096990Z         mock_arm_client.tenants.list.return_value = [TenantStub(self.tenant_id)]
2023-04-13T04:49:15.7097312Z         mock_arm_client.subscriptions.list.return_value = [deepcopy(self.subscription1_raw)]
2023-04-13T04:49:15.7097617Z         create_subscription_client_mock.return_value = mock_arm_client
2023-04-13T04:49:15.7097828Z     
2023-04-13T04:49:15.7097988Z         cli = DummyCli()
2023-04-13T04:49:15.7098264Z         storage_mock = {'subscriptions': []}
2023-04-13T04:49:15.7098518Z         profile = Profile(cli_ctx=cli, storage=storage_mock)
2023-04-13T04:49:15.7098838Z         consolidated = profile._normalize_properties(self.user1, deepcopy([self.subscription1]), False, None, None)
2023-04-13T04:49:15.7099145Z         profile._set_subscriptions(consolidated)
2023-04-13T04:49:15.7099353Z     
2023-04-13T04:49:15.7099596Z         mock_arm_client.tenants.list.return_value = [TenantStub(self.tenant_id)]
2023-04-13T04:49:15.7099948Z         mock_arm_client.subscriptions.list.return_value = deepcopy([self.subscription1_raw, self.subscription2_raw])
2023-04-13T04:49:15.7100206Z     
2023-04-13T04:49:15.7100377Z >       profile.refresh_accounts()
2023-04-13T04:49:15.7100471Z 
2023-04-13T04:49:15.7100805Z /opt/az/lib/python3.10/site-packages/azure/cli/core/tests/test_profile.py:1270: 
2023-04-13T04:49:15.7101244Z _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
2023-04-13T04:49:15.7101628Z /opt/az/lib/python3.10/site-packages/azure/cli/core/_profile.py:640: in refresh_accounts
2023-04-13T04:49:15.7101917Z     self._set_subscriptions(result, merge=False)
2023-04-13T04:49:15.7102284Z /opt/az/lib/python3.10/site-packages/azure/cli/core/_profile.py:496: in _set_subscriptions
2023-04-13T04:49:15.7102591Z     set_cloud_subscription(self.cli_ctx, active_cloud.name, default_sub_id)
2023-04-13T04:49:15.7102976Z /opt/az/lib/python3.10/site-packages/azure/cli/core/cloud.py:576: in set_cloud_subscription
2023-04-13T04:49:15.7103254Z     if not _get_cloud(cli_ctx, cloud_name):
2023-04-13T04:49:15.7103604Z /opt/az/lib/python3.10/site-packages/azure/cli/core/cloud.py:491: in _get_cloud
2023-04-13T04:49:15.7103896Z     return next((x for x in get_clouds(cli_ctx) if x.name == cloud_name), None)
2023-04-13T04:49:15.7104268Z /opt/az/lib/python3.10/site-packages/azure/cli/core/cloud.py:514: in get_clouds
2023-04-13T04:49:15.7104518Z     config.read(CLOUD_CONFIG_FILE)
2023-04-13T04:49:15.7104753Z /opt/az/lib/python3.10/configparser.py:699: in read
2023-04-13T04:49:15.7104980Z     self._read(fp, filename)
2023-04-13T04:49:15.7105200Z _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
2023-04-13T04:49:15.7105304Z 
2023-04-13T04:49:15.7105507Z self = <configparser.ConfigParser object at 0xffff9d0d9180>
2023-04-13T04:49:15.7105874Z fp = <_io.TextIOWrapper name='/root/.azure/clouds.config' mode='r' encoding='UTF-8'>
2023-04-13T04:49:15.7106179Z fpname = '/root/.azure/clouds.config'
2023-04-13T04:49:15.7106280Z 
2023-04-13T04:49:15.7106452Z     def _read(self, fp, fpname):
2023-04-13T04:49:15.7106666Z         """Parse a sectioned configuration file.
2023-04-13T04:49:15.7106856Z     
2023-04-13T04:49:15.7107070Z         Each section in a configuration file contains a header, indicated by
2023-04-13T04:49:15.7107349Z         a name in square brackets (`[]`), plus key/value options, indicated by
2023-04-13T04:49:15.7107609Z         `name` and `value` delimited with a specific substring (`=` or `:` by
2023-04-13T04:49:15.7107825Z         default).
2023-04-13T04:49:15.7107989Z     
2023-04-13T04:49:15.7108217Z         Values can span multiple lines, as long as they are indented deeper
2023-04-13T04:49:15.7108614Z         than the first line of the value. Depending on the parser's mode, blank
2023-04-13T04:49:15.7108897Z         lines may be treated as parts of multiline values or ignored.
2023-04-13T04:49:15.7109117Z     
2023-04-13T04:49:15.7109371Z         Configuration files may include comments, prefixed by specific
2023-04-13T04:49:15.7109797Z         characters (`#` and `;` by default). Comments may appear on their own
2023-04-13T04:49:15.7110090Z         in an otherwise empty line or may be entered in lines holding values or
2023-04-13T04:49:15.7110392Z         section names. Please note that comments get stripped off when reading configuration files.
2023-04-13T04:49:15.7110657Z         """
2023-04-13T04:49:15.7110857Z         elements_added = set()
2023-04-13T04:49:15.7111100Z         cursect = None                        # None, or a dictionary
2023-04-13T04:49:15.7111313Z         sectname = None
2023-04-13T04:49:15.7111511Z         optname = None
2023-04-13T04:49:15.7111708Z         lineno = 0
2023-04-13T04:49:15.7111913Z         indent_level = 0
2023-04-13T04:49:15.7112140Z         e = None                              # None, or an exception
2023-04-13T04:49:15.7112356Z         for lineno, line in enumerate(fp, start=1):
2023-04-13T04:49:15.7112686Z             comment_start = sys.maxsize
2023-04-13T04:49:15.7112890Z             # strip inline comments
2023-04-13T04:49:15.7113236Z             inline_prefixes = {p: -1 for p in self._inline_comment_prefixes}
2023-04-13T04:49:15.7113508Z             while comment_start == sys.maxsize and inline_prefixes:
2023-04-13T04:49:15.7113731Z                 next_prefixes = {}
2023-04-13T04:49:15.7114059Z                 for prefix, index in inline_prefixes.items():
2023-04-13T04:49:15.7114292Z                     index = line.find(prefix, index+1)
2023-04-13T04:49:15.7114494Z                     if index == -1:
2023-04-13T04:49:15.7114672Z                         continue
2023-04-13T04:49:15.7114866Z                     next_prefixes[prefix] = index
2023-04-13T04:49:15.7115182Z                     if index == 0 or (index > 0 and line[index-1].isspace()):
2023-04-13T04:49:15.7115430Z                         comment_start = min(comment_start, index)
2023-04-13T04:49:15.7115652Z                 inline_prefixes = next_prefixes
2023-04-13T04:49:15.7115858Z             # strip full line comments
2023-04-13T04:49:15.7116065Z             for prefix in self._comment_prefixes:
2023-04-13T04:49:15.7116291Z                 if line.strip().startswith(prefix):
2023-04-13T04:49:15.7116494Z                     comment_start = 0
2023-04-13T04:49:15.7116670Z                     break
2023-04-13T04:49:15.7116865Z             if comment_start == sys.maxsize:
2023-04-13T04:49:15.7117059Z                 comment_start = None
2023-04-13T04:49:15.7117282Z             value = line[:comment_start].strip()
2023-04-13T04:49:15.7117490Z             if not value:
2023-04-13T04:49:15.7117693Z                 if self._empty_lines_in_values:
2023-04-13T04:49:15.7117924Z                     # add empty line to the value, but only if there was no
2023-04-13T04:49:15.7118135Z                     # comment on the line
2023-04-13T04:49:15.7118336Z                     if (comment_start is None and
2023-04-13T04:49:15.7118541Z                         cursect is not None and
2023-04-13T04:49:15.7118727Z                         optname and
2023-04-13T04:49:15.7118919Z                         cursect[optname] is not None):
2023-04-13T04:49:15.7119230Z                         cursect[optname].append('') # newlines added at join
2023-04-13T04:49:15.7119438Z                 else:
2023-04-13T04:49:15.7119628Z                     # empty line marks end of value
2023-04-13T04:49:15.7119839Z                     indent_level = sys.maxsize
2023-04-13T04:49:15.7120018Z                 continue
2023-04-13T04:49:15.7120516Z             # continuation line?
2023-04-13T04:49:15.7120741Z             first_nonspace = self.NONSPACECRE.search(line)
2023-04-13T04:49:15.7121026Z             cur_indent_level = first_nonspace.start() if first_nonspace else 0
2023-04-13T04:49:15.7121274Z             if (cursect is not None and optname and
2023-04-13T04:49:15.7121485Z                 cur_indent_level > indent_level):
2023-04-13T04:49:15.7121698Z                 cursect[optname].append(value)
2023-04-13T04:49:15.7121913Z             # a section header or option header?
2023-04-13T04:49:15.7122297Z             else:
2023-04-13T04:49:15.7122485Z                 indent_level = cur_indent_level
2023-04-13T04:49:15.7122690Z                 # is it a section header?
2023-04-13T04:49:15.7122894Z                 mo = self.SECTCRE.match(value)
2023-04-13T04:49:15.7123088Z                 if mo:
2023-04-13T04:49:15.7123368Z                     sectname = mo.group('header')
2023-04-13T04:49:15.7123577Z                     if sectname in self._sections:
2023-04-13T04:49:15.7123806Z                         if self._strict and sectname in elements_added:
2023-04-13T04:49:15.7124058Z                             raise DuplicateSectionError(sectname, fpname,
2023-04-13T04:49:15.7124275Z                                                         lineno)
2023-04-13T04:49:15.7124485Z                         cursect = self._sections[sectname]
2023-04-13T04:49:15.7124713Z                         elements_added.add(sectname)
2023-04-13T04:49:15.7124936Z                     elif sectname == self.default_section:
2023-04-13T04:49:15.7125155Z                         cursect = self._defaults
2023-04-13T04:49:15.7125339Z                     else:
2023-04-13T04:49:15.7125516Z                         cursect = self._dict()
2023-04-13T04:49:15.7125724Z                         self._sections[sectname] = cursect
2023-04-13T04:49:15.7126119Z                         self._proxies[sectname] = SectionProxy(self, sectname)
2023-04-13T04:49:15.7126358Z                         elements_added.add(sectname)
2023-04-13T04:49:15.7126681Z                     # So sections can't start with a continuation line
2023-04-13T04:49:15.7126890Z                     optname = None
2023-04-13T04:49:15.7127088Z                 # no section header in the file?
2023-04-13T04:49:15.7127288Z                 elif cursect is None:
2023-04-13T04:49:15.7127513Z                     raise MissingSectionHeaderError(fpname, lineno, line)
2023-04-13T04:49:15.7127732Z                 # an option line?
2023-04-13T04:49:15.7127899Z                 else:
2023-04-13T04:49:15.7128096Z                     mo = self._optcre.match(value)
2023-04-13T04:49:15.7128288Z                     if mo:
2023-04-13T04:49:15.7128587Z                         optname, vi, optval = mo.group('option', 'vi', 'value')
2023-04-13T04:49:15.7128812Z                         if not optname:
2023-04-13T04:49:15.7129022Z                             e = self._handle_error(e, fpname, lineno, line)
2023-04-13T04:49:15.7129266Z                         optname = self.optionxform(optname.rstrip())
2023-04-13T04:49:15.7129486Z                         if (self._strict and
2023-04-13T04:49:15.7129699Z                             (sectname, optname) in elements_added):
2023-04-13T04:49:15.7129932Z                             raise DuplicateOptionError(sectname, optname,
2023-04-13T04:49:15.7130172Z                                                        fpname, lineno)
2023-04-13T04:49:15.7130388Z                         elements_added.add((sectname, optname))
2023-04-13T04:49:15.7130629Z                         # This check is fine because the OPTCRE cannot
2023-04-13T04:49:15.7130861Z                         # match if it would set optval to None
2023-04-13T04:49:15.7131064Z                         if optval is not None:
2023-04-13T04:49:15.7131280Z                             optval = optval.strip()
2023-04-13T04:49:15.7131489Z                             cursect[optname] = [optval]
2023-04-13T04:49:15.7131676Z                         else:
2023-04-13T04:49:15.7131870Z                             # valueless option handling
2023-04-13T04:49:15.7132067Z                             cursect[optname] = None
2023-04-13T04:49:15.7132254Z                     else:
2023-04-13T04:49:15.7132538Z                         # a non-fatal parsing error occurred. set up the
2023-04-13T04:49:15.7132786Z                         # exception but keep going. the exception will be
2023-04-13T04:49:15.7133028Z                         # raised at the end of the file and will contain a
2023-04-13T04:49:15.7133396Z                         # list of all bogus lines
2023-04-13T04:49:15.7133624Z                         e = self._handle_error(e, fpname, lineno, line)
2023-04-13T04:49:15.7133851Z         self._join_multiline_values()
2023-04-13T04:49:15.7134078Z         # if any parsing errors occurred, raise an exception
2023-04-13T04:49:15.7134286Z         if e:
2023-04-13T04:49:15.7134444Z >           raise e
2023-04-13T04:49:15.7134813Z E           configparser.ParsingError: Source contains parsing errors: '/root/.azure/clouds.config'
2023-04-13T04:49:15.7135148Z E           	[line  4]: 'rosoft.com\n'
2023-04-13T04:49:15.7135232Z 
2023-04-13T04:49:15.7135457Z /opt/az/lib/python3.10/configparser.py:1118: ParsingError
2023-04-13T04:49:15.7135826Z -------- generated xml file: /azure_cli_test_result/azure-cli-core.xml ---------

Testing Guide

History Notes

[Component Name 1] BREAKING CHANGE: az command a: Make some customer-facing breaking change
[Component Name 2] az command b: Add some customer-facing feature


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

@azure-client-tools-bot-prd
Copy link

azure-client-tools-bot-prd bot commented Apr 13, 2023

️✔️acr
️✔️2020-09-01-hybrid
️✔️3.10
️✔️3.9
️✔️latest
️✔️3.10
️✔️3.9
️✔️acs
️✔️2020-09-01-hybrid
️✔️3.10
️✔️3.9
️✔️latest
️✔️3.10
️✔️3.9
️✔️advisor
️✔️latest
️✔️3.10
️✔️3.9
️✔️ams
️✔️latest
️✔️3.10
️✔️3.9
️✔️apim
️✔️latest
️✔️3.10
️✔️3.9
️✔️appconfig
️✔️latest
️✔️3.10
️✔️3.9
️✔️appservice
️✔️latest
️✔️3.10
️✔️3.9
️✔️aro
️✔️latest
️✔️3.10
️✔️3.9
️✔️backup
️✔️latest
️✔️3.10
️✔️3.9
️✔️batch
️✔️latest
️✔️3.10
️✔️3.9
️✔️batchai
️✔️latest
️✔️3.10
️✔️3.9
️✔️billing
️✔️latest
️✔️3.10
️✔️3.9
️✔️botservice
️✔️latest
️✔️3.10
️✔️3.9
️✔️cdn
️✔️latest
️✔️3.10
️✔️3.9
️✔️cloud
️✔️latest
️✔️3.10
️✔️3.9
️✔️cognitiveservices
️✔️latest
️✔️3.10
️✔️3.9
️✔️config
️✔️latest
️✔️3.10
️✔️3.9
️✔️configure
️✔️latest
️✔️3.10
️✔️3.9
️✔️consumption
️✔️latest
️✔️3.10
️✔️3.9
️✔️container
️✔️latest
️✔️3.10
️✔️3.9
️✔️core
️✔️2018-03-01-hybrid
️✔️3.10
️✔️3.9
️✔️2019-03-01-hybrid
️✔️3.10
️✔️3.9
️✔️2020-09-01-hybrid
️✔️3.10
️✔️3.9
️✔️latest
️✔️3.10
️✔️3.9
️✔️cosmosdb
️✔️latest
️✔️3.10
️✔️3.9
️✔️databoxedge
️✔️2019-03-01-hybrid
️✔️3.10
️✔️3.9
️✔️2020-09-01-hybrid
️✔️3.10
️✔️3.9
️✔️latest
️✔️3.10
️✔️3.9
️✔️dla
️✔️latest
️✔️3.10
️✔️3.9
️✔️dls
️✔️latest
️✔️3.10
️✔️3.9
️✔️dms
️✔️latest
️✔️3.10
️✔️3.9
️✔️eventgrid
️✔️latest
️✔️3.10
️✔️3.9
️✔️eventhubs
️✔️latest
️✔️3.10
️✔️3.9
️✔️feedback
️✔️latest
️✔️3.10
️✔️3.9
️✔️find
️✔️latest
️✔️3.10
️✔️3.9
️✔️hdinsight
️✔️latest
️✔️3.10
️✔️3.9
️✔️identity
️✔️latest
️✔️3.10
️✔️3.9
️✔️iot
️✔️2019-03-01-hybrid
️✔️3.10
️✔️3.9
️✔️2020-09-01-hybrid
️✔️3.10
️✔️3.9
️✔️latest
️✔️3.10
️✔️3.9
️✔️keyvault
️✔️2018-03-01-hybrid
️✔️3.10
️✔️3.9
️✔️2020-09-01-hybrid
️✔️3.10
️✔️3.9
️✔️latest
️✔️3.10
️✔️3.9
️✔️kusto
️✔️latest
️✔️3.10
️✔️3.9
️✔️lab
️✔️latest
️✔️3.10
️✔️3.9
️✔️managedservices
️✔️latest
️✔️3.10
️✔️3.9
️✔️maps
️✔️latest
️✔️3.10
️✔️3.9
️✔️marketplaceordering
️✔️latest
️✔️3.10
️✔️3.9
️✔️monitor
️✔️latest
️✔️3.10
️✔️3.9
️✔️netappfiles
️✔️latest
️✔️3.10
️✔️3.9
️✔️network
️✔️2018-03-01-hybrid
️✔️3.10
️✔️3.9
️✔️latest
️✔️3.10
️✔️3.9
️✔️policyinsights
️✔️latest
️✔️3.10
️✔️3.9
️✔️privatedns
️✔️latest
️✔️3.10
️✔️3.9
️✔️profile
️✔️latest
️✔️3.10
️✔️3.9
️✔️rdbms
️✔️latest
️✔️3.10
️✔️3.9
️✔️redis
️✔️latest
️✔️3.10
️✔️3.9
️✔️relay
️✔️latest
️✔️3.10
️✔️3.9
️✔️resource
️✔️2018-03-01-hybrid
️✔️3.10
️✔️3.9
️✔️2019-03-01-hybrid
️✔️3.10
️✔️3.9
️✔️latest
️✔️3.10
️✔️3.9
️✔️role
️✔️latest
️✔️3.10
️✔️3.9
️✔️search
️✔️latest
️✔️3.10
️✔️3.9
️✔️security
️✔️latest
️✔️3.10
️✔️3.9
️✔️servicebus
️✔️latest
️✔️3.10
️✔️3.9
️✔️serviceconnector
️✔️latest
️✔️3.10
️✔️3.9
️✔️servicefabric
️✔️latest
️✔️3.10
️✔️3.9
️✔️signalr
️✔️latest
️✔️3.10
️✔️3.9
️✔️sql
️✔️latest
️✔️3.10
️✔️3.9
️✔️sqlvm
️✔️latest
️✔️3.10
️✔️3.9
️✔️storage
️✔️2018-03-01-hybrid
️✔️3.10
️✔️3.9
️✔️2019-03-01-hybrid
️✔️3.10
️✔️3.9
️✔️2020-09-01-hybrid
️✔️3.10
️✔️3.9
️✔️latest
️✔️3.10
️✔️3.9
️✔️synapse
️✔️latest
️✔️3.10
️✔️3.9
️✔️telemetry
️✔️2018-03-01-hybrid
️✔️3.10
️✔️3.9
️✔️2019-03-01-hybrid
️✔️3.10
️✔️3.9
️✔️2020-09-01-hybrid
️✔️3.10
️✔️3.9
️✔️latest
️✔️3.10
️✔️3.9
️✔️util
️✔️latest
️✔️3.10
️✔️3.9
️✔️vm
️✔️2018-03-01-hybrid
️✔️3.10
️✔️3.9
️✔️2019-03-01-hybrid
️✔️3.10
️✔️3.9
️✔️2020-09-01-hybrid
️✔️3.10
️✔️3.9
️✔️latest
️✔️3.10
️✔️3.9

@azure-client-tools-bot-prd
Copy link

Hi @bebound,
Since the current milestone time is less than 7 days, this pr will be reviewed in the next milestone.

@ghost ghost added the Auto-Assign Auto assign by bot label Apr 13, 2023
@ghost ghost requested review from jiasli and yonzhan April 13, 2023 08:29
@ghost ghost assigned jiasli Apr 13, 2023
@ghost ghost added the Account az login/account label Apr 13, 2023
@yonzhan
Copy link
Collaborator

yonzhan commented Apr 13, 2023

Core

@ghost ghost added the Core CLI core infrastructure label Apr 13, 2023
os.makedirs(GLOBAL_CONFIG_DIR)
with open(CLOUD_CONFIG_FILE, 'w') as configfile:
if not os.path.isdir(os.path.dirname(get_cloud_config_file(cli_ctx))):
os.makedirs(os.path.dirname(get_cloud_config_file(cli_ctx)))
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 folder has been created by

azure_folder = self.config.config_dir
ensure_dir(azure_folder)

I don't know if this function is called when cli_ctx does not exist.

@@ -18,8 +18,14 @@

logger = get_logger(__name__)

# This default cloud name. If you use DummyCli(random_config_dir=True), use get_cloud_config_file() instead.
CLOUD_CONFIG_FILE = os.path.join(GLOBAL_CONFIG_DIR, 'clouds.config')
Copy link
Member

@jiasli jiasli Apr 18, 2023

Choose a reason for hiding this comment

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

The original implementation is wrong.

get_default_cli uses GLOBAL_CONFIG_DIR for knack.cli.CLI.__init__'s config_dir argument:

return AzCli(cli_name='az',
config_dir=GLOBAL_CONFIG_DIR,

The location of clouds.config is also built from GLOBAL_CONFIG_DIR.

If knack.cli.CLI.__init__'s config_dir doesn't use GLOBAL_CONFIG_DIR, but another location, like what has been done in #25689:

config_dir=os.path.join(GLOBAL_CONFIG_DIR, 'dummy_cli_config_dir',
random_string()) if random_config_dir else GLOBAL_CONFIG_DIR,

Then the location of clouds.config will still be under GLOBAL_CONFIG_DIR, because it directly uses CLOUD_CONFIG_FILE, instead of cli_ctx.config.config_dir. This defeats the purpose of overriding config_dir.

Copy link
Member

Choose a reason for hiding this comment

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

profile._storage is an azure.cli.core._session.ACCOUNT which is a persisted JSON. It can easily be replaced by an in-memory dict during tests, but cloud.config is an INI file, which introduces inconsistency. See #14436 (comment)

Comment on lines +25 to +26
def get_cloud_config_file(cli_ctx=None):
return os.path.join(cli_ctx.config.config_dir, 'clouds.config') if cli_ctx else CLOUD_CONFIG_FILE
Copy link
Member

Choose a reason for hiding this comment

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

Calling this function multiple times is tedious. Perhaps we simply save it as cli_ctx.cloud_config?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Account az login/account Auto-Assign Auto assign by bot Core CLI core infrastructure
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants