From 37da4bf605ddedc24f3f31a061b4c7c175c7ba26 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 22 Sep 2026 04:20:04 +0100 Subject: [PATCH] fix(generator): preserve nonexclusive gcloud update groups --- .../gcloud-managed-kafka-clusters-update.txt | 161 ++++++++++++++++++ .../GcloudNestedSynopsisValidationTests.cs | 19 +++ .../Scrapers/Cli/GcloudCliScraper.cs | 6 +- 3 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Fixtures/Gcloud/585.0.0/gcloud-managed-kafka-clusters-update.txt diff --git a/tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Fixtures/Gcloud/585.0.0/gcloud-managed-kafka-clusters-update.txt b/tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Fixtures/Gcloud/585.0.0/gcloud-managed-kafka-clusters-update.txt new file mode 100644 index 00000000000..b2ff396a95c --- /dev/null +++ b/tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Fixtures/Gcloud/585.0.0/gcloud-managed-kafka-clusters-update.txt @@ -0,0 +1,161 @@ +NAME + gcloud managed-kafka clusters update - update a Managed Service for Apache + Kafka cluster + +SYNOPSIS + gcloud managed-kafka clusters update (CLUSTER : --location=LOCATION) + (--allowed-source-ip-ranges=[ALLOWED_SOURCE_IP_RANGES,...] + --auto-rebalance --broker-disk=BROKER_DISK --cpu=CPU + --labels=[KEY=VALUE,...] --memory=MEMORY --[no-]public-cluster + --ssl-principal-mapping-rules=SSL_PRINCIPAL_MAPPING_RULES + --subnets=[SUBNETS,...] --clear-mtls-ca-pools + | --mtls-ca-pools=[MTLS_CA_POOLS,...]) + [--allow-broker-downscale-on-cluster-upscale] [--async] + [GCLOUD_WIDE_FLAG ...] + +DESCRIPTION + Update a Managed Service for Apache Kafka cluster. + +EXAMPLES + To update an attribute in a cluster named mycluster located in us-central1, + such as the CPU, run the following: + + $ gcloud managed-kafka clusters update mycluster \ + --location=us-central1 --cpu=3 + +POSITIONAL ARGUMENTS + Cluster resource - Identifies the cluster to be updated. The arguments in + this group can be used to specify the attributes of this resource. (NOTE) + Some attributes are not given arguments in this group but can be set in + other ways. + + To set the project attribute: + * provide the argument cluster on the command line with a fully + specified name; + * provide the argument --project on the command line; + * set the property core/project. + + This must be specified. + + CLUSTER + ID of the cluster or fully qualified identifier for the cluster. + + To set the cluster attribute: + + provide the argument cluster on the command line. + + This positional argument must be specified if any of the other + arguments in this group are specified. + + --location=LOCATION + ID of the location of the Managed Service for Apache Kafka resource. + See + https://cloud.google.com/managed-service-for-apache-kafka/docs/locations + for a list of supported locations. + + To set the location attribute: + + provide the argument cluster on the command line with a fully + specified name; + + provide the argument --location on the command line. + +REQUIRED FLAGS + At least one of these must be specified: + + --allowed-source-ip-ranges=[ALLOWED_SOURCE_IP_RANGES,...] + A comma-separated list of IPv4 ranges in CIDR notation that are + allowed to connect to the public cluster. Use this flag only if + --public-cluster is enabled. Example: + --allowed-source-ip-ranges=203.0.113.0/24,198.51.100.0/24 + + --auto-rebalance + Whether the automatic rebalancing is enabled. If automatic + rebalancing is enabled, topic partitions are rebalanced among brokers + when the number of CPUs in the cluster changes. Automatic rebalancing + is enabled by default. Use --no-auto-rebalance to disable this flag. + + --broker-disk=BROKER_DISK + The amount of local disk to provision for each broker. Can be + specified as a plain integer (defaults to GiB) or with units (e.g., + 500Gi, 500GiB, 1Ti, 1TiB). Minimum: 100 Gibibytes. + + --cpu=CPU + The number of vCPUs to provision for the cluster. The minimum is 3. + + --labels=[KEY=VALUE,...] + List of label KEY=VALUE pairs to add. Keys must start with a + lowercase character and contain only hyphens (-), underscores (_), + lowercase characters, and numbers. Values must contain only hyphens + (-), underscores (_), lowercase characters, and numbers. + + --memory=MEMORY + The memory to provision for the cluster in bytes. The value must be + between 1 GiB and 8 GiB per vCPU. Ex. 1024Mi, 4Gi. + + --[no-]public-cluster + Enable a public cluster. If disabled, public cluster config is + cleared. Use --public-cluster to enable and --no-public-cluster to + disable. + + --ssl-principal-mapping-rules=SSL_PRINCIPAL_MAPPING_RULES + The rules for mapping mTLS certificate Distinguished Names (DNs) to + shortened principal names for Kafka ACLs. This flag corresponds + exactly to the ssl.principal.mapping.rules broker config and matches + the format and syntax defined in the Apache Kafka documentation. + Setting or modifying this field will trigger a rolling restart of the + Kafka brokers to apply the change. An empty string means that the + default Kafka behavior is used. Example: + "RULE:^CN=(.?),OU=ServiceUsers.$/$1@example.com/,DEFAULT" + + --subnets=[SUBNETS,...] + A comma-separated list of VPC subnets from which the cluster is + accessible. Both broker and bootstrap server IP addresses and DNS + entries are automatically created in each subnet. Only one subnet per + network is allowed, and the subnet must be located in the same region + as the cluster. The project may differ. A minimum of 1 subnet is + required. A maximum of 10 subnets can be specified. Use commas to + separate multiple subnets. The name of the subnet must be in the + format projects/PROJECT_ID/regions/REGION/subnetworks/SUBNET. + + At most one of these can be specified: + + --clear-mtls-ca-pools + Remove all the CA pools from the cluster. This will remove all root + certificates from the truststore of each broker in the cluster. + + --mtls-ca-pools=[MTLS_CA_POOLS,...] + A comma-separated list of CA pools from the Google Cloud + Certificate Authority Service. The root certificates of these CA + pools will be installed in the truststore of each broker in the + cluster for use with mTLS. A maximum of 10 CA pools can be + specified. CA pools can be in a different project and region than + the cluster. This command overwrites the entire set of pools + currently configured on the cluster. If you want to add a new pool + to an existing configuration, you must provide the full list of + both the old and new CA pools in the command. Each CA pool must be + in the format + projects/PROJECT_ID/locations/LOCATION/caPools/CA_POOL. Clear the + CA pools using the --clear-mtls-ca-pools flag. + +OPTIONAL FLAGS + --allow-broker-downscale-on-cluster-upscale + If enabled, this setting allows an update operation that could + significantly decrease the per-broker vCPU and/or memory allocation, + which can lead to reduced performance and availability. By default, an + update operation will fail if it results in a reduction of 10% or more + to the brokers' vCPU or memory allocation. + + --async + Return immediately, without waiting for the operation in progress to + complete. + +GCLOUD WIDE FLAGS + These flags are available to all commands: --access-token-file, --account, + --billing-project, --configuration, --flags-file, --flatten, --format, + --help, --impersonate-service-account, --log-http, --project, --quiet, + --trace-token, --user-output-enabled, --verbosity. + + Run $ gcloud help for details. + +API REFERENCE + This command uses the managedkafka/v1 API. The full documentation for this + API can be found at: + https://cloud.google.com/managed-service-for-apache-kafka/docs diff --git a/tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Generators/GcloudNestedSynopsisValidationTests.cs b/tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Generators/GcloudNestedSynopsisValidationTests.cs index a38c98e204c..925c753f1e1 100644 --- a/tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Generators/GcloudNestedSynopsisValidationTests.cs +++ b/tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Generators/GcloudNestedSynopsisValidationTests.cs @@ -4,6 +4,25 @@ namespace ModularPipelines.OptionsGenerator.Tests.Generators; public partial class RequiredConstructorValidationTests { + [Test] + public async Task Gcloud_Kafka_Updates_Preserve_Nonexclusive_Settings_And_Exclusive_Ca_Pools() + { + var command = await GcloudCapturedSemanticsTests.Scrape("managed-kafka clusters update"); + var group = command.RequiredAlternativeGroups.Single(group => group.PropertyNames.Contains("Cpu")); + await ValidateCapturedGroup(command, group, + [ + ("Cpu,MtlsCaPools", true), + ("", false), + ("Cpu", true), + ("MtlsCaPools", true), + ("ClearMtlsCaPools", true), + ("Cpu,ClearMtlsCaPools", true), + ("Cpu,Memory", true), + ("ClearMtlsCaPools,MtlsCaPools", false), + ("Cpu,ClearMtlsCaPools,MtlsCaPools", false), + ]); + } + [Test] public async Task Gcloud_Composer_Updates_Preserve_Optional_Resource_Settings() { diff --git a/tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/GcloudCliScraper.cs b/tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/GcloudCliScraper.cs index 89e72f5c64c..77f24446ef8 100644 --- a/tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/GcloudCliScraper.cs +++ b/tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/GcloudCliScraper.cs @@ -414,9 +414,9 @@ private static CliRequiredAlternativeGroup PreserveDocumentedChoices(CliRequired IReadOnlyList documented) { group = RestoreFlattenedDocumentedChoices(group, documented); - // Colon syntax can hide a documented, nonexclusive "at least one" rule. - // Preserve that cardinality instead of requiring every member of the bundle. - var choice = group.IsChoice ? null : documented.FirstOrDefault(candidate => candidate.IsChoice + // Flattened synopsis syntax can hide a documented, nonexclusive "at least one" + // rule. Preserve its complete shape, including exclusive nested alternatives. + var choice = documented.FirstOrDefault(candidate => candidate.IsChoice && !candidate.IsMutuallyExclusive && candidate.Members.All(member => !member.IsRequired) && group.PropertyNames.ToHashSet(StringComparer.Ordinal).SetEquals(candidate.PropertyNames));