From b34ca295cb18cac7b853883cda21e516070213df Mon Sep 17 00:00:00 2001 From: Paul Meyer <49727155+katexochen@users.noreply.github.com> Date: Thu, 14 Dec 2023 22:17:46 +0100 Subject: [PATCH] azure-cli: immutable command index --- ...optional-immutable-configuration-dir.patch | 98 +++++++++++++++++++ pkgs/tools/admin/azure-cli/default.nix | 20 +++- .../tools/admin/azure-cli/python-packages.nix | 13 ++- 3 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 pkgs/tools/admin/azure-cli/0001-optional-immutable-configuration-dir.patch diff --git a/pkgs/tools/admin/azure-cli/0001-optional-immutable-configuration-dir.patch b/pkgs/tools/admin/azure-cli/0001-optional-immutable-configuration-dir.patch new file mode 100644 index 000000000000..e163915d1c72 --- /dev/null +++ b/pkgs/tools/admin/azure-cli/0001-optional-immutable-configuration-dir.patch @@ -0,0 +1,98 @@ +From c12adfdefd8a091e1fa870305a3cc61de6426914 Mon Sep 17 00:00:00 2001 +From: Paul Meyer <49727155+katexochen@users.noreply.github.com> +Date: Thu, 14 Dec 2023 21:16:20 +0100 +Subject: [PATCH] optional immutable configuration dir + +Adding the possibility to configure an immutable configuration dir via +env variable `AZURE_IMMUTABLE_DIR`. This path is used for the files +that configure the dynamic behavior of the CLI code. +An immutable session is used for these files to ensure we don't try to +write to them. + +Signed-off-by: Paul Meyer <49727155+katexochen@users.noreply.github.com> +--- + azure/cli/core/__init__.py | 5 +++-- + azure/cli/core/_session.py | 19 ++++++++++++++++--- + .../cli/core/extension/dynamic_install.py | 3 ++- + 3 files changed, 21 insertions(+), 6 deletions(-) + +diff --git a/azure/cli/core/__init__.py b/azure/cli/core/__init__.py +index d112633ec..20b6d045b 100644 +--- a/azure/cli/core/__init__.py ++++ b/azure/cli/core/__init__.py +@@ -75,12 +75,13 @@ class AzCli(CLI): + self.data['query_active'] = False + + azure_folder = self.config.config_dir ++ azure_immutable_folder = os.environ.get('AZURE_IMMUTABLE_DIR', azure_folder) + ensure_dir(azure_folder) + ACCOUNT.load(os.path.join(azure_folder, 'azureProfile.json')) + CONFIG.load(os.path.join(azure_folder, 'az.json')) + SESSION.load(os.path.join(azure_folder, 'az.sess'), max_age=3600) +- INDEX.load(os.path.join(azure_folder, 'commandIndex.json')) +- VERSIONS.load(os.path.join(azure_folder, 'versionCheck.json')) ++ INDEX.load(os.path.join(azure_immutable_folder, 'commandIndex.json')) ++ VERSIONS.load(os.path.join(azure_immutable_folder, 'versionCheck.json')) + handle_version_update() + + self.cloud = get_active_cloud(self) +diff --git a/azure/cli/core/_session.py b/azure/cli/core/_session.py +index 471a0344c..acaef6fb8 100644 +--- a/azure/cli/core/_session.py ++++ b/azure/cli/core/_session.py +@@ -85,6 +85,19 @@ class Session(MutableMapping): + return len(self.data) + + ++class ImmutableSession(Session): ++ """ ++ A session that is backed by an immutable JSON file. This session is read-only. ++ """ ++ def save(self): ++ if os.getenv('AZURE_IMMUTABLE_DIR'): ++ get_logger(__name__).log(logging.DEBUG, ++ "Skipping update of file %s due to immutable directory.", ++ self.filename) ++ return ++ super().save() ++ ++ + # ACCOUNT contains subscriptions information + ACCOUNT = Session() + +@@ -95,16 +108,16 @@ CONFIG = Session() + SESSION = Session() + + # INDEX contains {top-level command: [command_modules and extensions]} mapping index +-INDEX = Session() ++INDEX = ImmutableSession() + + # VERSIONS provides local versions and pypi versions. + # DO NOT USE it to get the current version of azure-cli, + # it could be lagged behind and can be used to check whether + # an upgrade of azure-cli happens +-VERSIONS = Session() ++VERSIONS = ImmutableSession() + + # EXT_CMD_TREE provides command to extension name mapping +-EXT_CMD_TREE = Session() ++EXT_CMD_TREE = ImmutableSession() + + # CLOUD_ENDPOINTS provides endpoints/suffixes of clouds + CLOUD_ENDPOINTS = Session() +diff --git a/azure/cli/core/extension/dynamic_install.py b/azure/cli/core/extension/dynamic_install.py +index cb03980a0..29279be2b 100644 +--- a/azure/cli/core/extension/dynamic_install.py ++++ b/azure/cli/core/extension/dynamic_install.py +@@ -17,7 +17,8 @@ def _get_extension_command_tree(cli_ctx): + VALID_SECOND = 3600 * 24 * 10 + if not cli_ctx: + return None +- EXT_CMD_TREE.load(os.path.join(cli_ctx.config.config_dir, 'extensionCommandTree.json'), VALID_SECOND) ++ azure_immutable_folder = os.environ.get('AZURE_IMMUTABLE_DIR', cli_ctx.config.config_dir) ++ EXT_CMD_TREE.load(os.path.join(azure_immutable_folder, 'extensionCommandTree.json'), VALID_SECOND) + if not EXT_CMD_TREE.data: + import posixpath + import requests +-- +2.42.0 diff --git a/pkgs/tools/admin/azure-cli/default.nix b/pkgs/tools/admin/azure-cli/default.nix index 71c478d61216..6c540df76540 100644 --- a/pkgs/tools/admin/azure-cli/default.nix +++ b/pkgs/tools/admin/azure-cli/default.nix @@ -2,6 +2,10 @@ , callPackage , fetchFromGitHub , installShellFiles + + # Whether to include patches that enable placing certain behavior-defining + # configuration files in the Nix store. +, withImmutableConfig ? true }: let @@ -177,7 +181,13 @@ py.pkgs.toPythonApplication (py.pkgs.buildAzureCliPackage { --replace register-python-argcomplete ${py.pkgs.argcomplete}/bin/register-python-argcomplete installShellCompletion --bash --name az.bash az.completion.sh installShellCompletion --zsh --name _az az.completion.sh - + '' + lib.optionalString withImmutableConfig '' + export HOME=$TMPDIR + $out/bin/az --version + mkdir -p $out/etc/azure + mv $TMPDIR/.azure/commandIndex.json $out/etc/azure/commandIndex.json + mv $TMPDIR/.azure/versionCheck.json $out/etc/azure/versionCheck.json + '' + '' # remove garbage rm $out/bin/az.bat rm $out/bin/az.completion.sh @@ -187,8 +197,12 @@ py.pkgs.toPythonApplication (py.pkgs.buildAzureCliPackage { # it's just a shebang script which calls `python -m azure.cli "$@"` postFixup = '' wrapProgram $out/bin/az \ - --set PYTHONPATH $PYTHONPATH - ''; + '' + lib.optionalString withImmutableConfig '' + --set AZURE_IMMUTABLE_DIR $out/etc/azure \ + '' + '' + --set PYTHONPATH $PYTHONPATH + '' + ; doInstallCheck = true; installCheckPhase = '' diff --git a/pkgs/tools/admin/azure-cli/python-packages.nix b/pkgs/tools/admin/azure-cli/python-packages.nix index 3714c5bec020..318b5044a8e3 100644 --- a/pkgs/tools/admin/azure-cli/python-packages.nix +++ b/pkgs/tools/admin/azure-cli/python-packages.nix @@ -1,4 +1,5 @@ -{ stdenv +{ lib +, stdenv , python3 , fetchPypi , fetchpatch @@ -30,6 +31,16 @@ let sourceRoot = "${src.name}/src/azure-cli-core"; + patches = [ + # Adding the possibility to configure an immutable configuration dir via `AZURE_IMMUTABLE_DIR`. + # This enables us to place configuration files that alter the behavior of the CLI in the Nix store. + # + # This is a downstream patch without an commit or PR upstream. + # There is an issue to discuss possible solutions upstream: + # https://github.com/Azure/azure-cli/issues/28093 + ./0001-optional-immutable-configuration-dir.patch + ]; + propagatedBuildInputs = with self; [ argcomplete azure-cli-telemetry