Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions linter_exclusions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ aks create:
node_public_ip_prefix_id:
rule_exclusions:
- option_length_too_long
node_public_ip_prefix_ids:
rule_exclusions:
- option_length_too_long
enable_private_cluster:
rule_exclusions:
- option_length_too_long
Expand Down Expand Up @@ -130,6 +133,11 @@ aks create:
service_account_image_pull_default_managed_identity_id:
rule_exclusions:
- option_length_too_long
aks nodepool add:
parameters:
node_public_ip_prefix_ids:
rule_exclusions:
- option_length_too_long
aks update:
parameters:
aad_admin_group_object_ids:
Expand Down
5 changes: 5 additions & 0 deletions src/aks-preview/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ Pending
+++++++
* `az aks create`: Add `--control-plane-scaling-size` parameter to configure control plane scaling profile with available sizes 'H2', 'H4', and 'H8'.

20.0.0b5
+++++++
* `az aks create`: Add `--node-public-ip-prefix-ids` parameter for specifying dual-stack (IPv4/IPv6) public IP prefixes for instance-level public IPs.
* `az aks nodepool add`: Add `--node-public-ip-prefix-ids` parameter for specifying dual-stack (IPv4/IPv6) public IP prefixes for instance-level public IPs.

20.0.0b4
+++++++
* `az aks nodepool update`: Support `--node-vm-size` to resize VM size of an existing VMSS-based agent pool (preview). Requires AFEC registration `Microsoft.ContainerService/AgentPoolVMSSResize`.
Expand Down
8 changes: 8 additions & 0 deletions src/aks-preview/azext_aks_preview/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,10 @@
- name: --node-public-ip-prefix-id
type: string
short-summary: Public IP prefix ID used to assign public IPs to VMSS nodes.
- name: --node-public-ip-prefix-ids
type: string
short-summary: Comma-separated list of public IP prefix resource IDs for dual-stack node public IPs (IPv4 and/or IPv6).
long-summary: At most one IPv4 and one IPv6 prefix may be specified. Automatically enables --enable-node-public-ip. Cannot be used with --node-public-ip-prefix-id. Requires the NodePublicIPv6PrefixPreview feature flag.
- name: --enable-managed-identity
type: bool
short-summary: Using managed identity to manage cluster resource group. You can explicitly specify "--service-principal" and "--client-secret" to disable managed identity, otherwise it will be enabled.
Expand Down Expand Up @@ -2155,6 +2159,10 @@
- name: --node-public-ip-prefix-id
type: string
short-summary: Public IP prefix ID used to assign public IPs to VMSS nodes. Must use VMSS agent pool type.
- name: --node-public-ip-prefix-ids
type: string
short-summary: Comma-separated list of public IP prefix resource IDs for dual-stack node public IPs (IPv4 and/or IPv6).
long-summary: At most one IPv4 and one IPv6 prefix may be specified. Automatically enables --enable-node-public-ip. Cannot be used with --node-public-ip-prefix-id. Requires the NodePublicIPv6PrefixPreview feature flag.
- name: --labels
type: string
short-summary: The node labels for the node pool. See https://aka.ms/node-labels for syntax of labels.
Expand Down
17 changes: 17 additions & 0 deletions src/aks-preview/azext_aks_preview/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@
validate_load_balancer_sku,
validate_max_surge,
validate_message_of_the_day,
validate_node_public_ip_prefix_ids,
validate_node_public_ip_tags,
validate_nodepool_id,
validate_nodepool_labels,
Expand Down Expand Up @@ -868,6 +869,14 @@ def load_arguments(self, _):
)
c.argument("enable_node_public_ip", action="store_true")
c.argument("node_public_ip_prefix_id")
c.argument(
"node_public_ip_prefix_ids",
validator=validate_node_public_ip_prefix_ids,
help="Comma-separated list of public IP prefix resource IDs for dual-stack node public IPs "
"(IPv4 and/or IPv6). At most one IPv4 and one IPv6 prefix may be specified. "
"Automatically enables --enable-node-public-ip. Cannot be used with --node-public-ip-prefix-id. "
"Requires the NodePublicIPv6PrefixPreview feature flag to be registered.",
)
Comment thread
christine33-creator marked this conversation as resolved.
c.argument("enable_cluster_autoscaler", action="store_true")
c.argument("min_count", type=int, validator=validate_nodes_count)
c.argument("max_count", type=int, validator=validate_nodes_count)
Expand Down Expand Up @@ -2042,6 +2051,14 @@ def load_arguments(self, _):
)
c.argument("enable_node_public_ip", action="store_true")
c.argument("node_public_ip_prefix_id")
c.argument(
"node_public_ip_prefix_ids",
validator=validate_node_public_ip_prefix_ids,
help="Comma-separated list of public IP prefix resource IDs for dual-stack node public IPs "
"(IPv4 and/or IPv6). At most one IPv4 and one IPv6 prefix may be specified. "
"Automatically enables --enable-node-public-ip. Cannot be used with --node-public-ip-prefix-id. "
"Requires the NodePublicIPv6PrefixPreview feature flag to be registered.",
)
c.argument(
"enable_cluster_autoscaler",
options_list=["--enable-cluster-autoscaler", "-e"],
Expand Down
26 changes: 26 additions & 0 deletions src/aks-preview/azext_aks_preview/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,32 @@ def validate_node_public_ip_tags(ns):
ns.node_public_ip_tags = tags_dict


def validate_node_public_ip_prefix_ids(ns):
"""Validate --node-public-ip-prefix-ids value and mutual exclusion with --node-public-ip-prefix-id."""
ids_value = getattr(ns, "node_public_ip_prefix_ids", None)
singular_value = getattr(ns, "node_public_ip_prefix_id", None)
if ids_value and singular_value:
Comment thread
christine33-creator marked this conversation as resolved.
raise MutuallyExclusiveArgumentError(
"--node-public-ip-prefix-ids and --node-public-ip-prefix-id cannot be used at the same time."
)
if ids_value is not None:
parsed = [x.strip() for x in ids_value.split(",") if x.strip()] if isinstance(ids_value, str) else ids_value
if not parsed:
raise InvalidArgumentValueError(
"--node-public-ip-prefix-ids must contain at least one public IP prefix resource ID."
)
if len(parsed) > 2:
raise InvalidArgumentValueError(
"--node-public-ip-prefix-ids accepts at most two public IP prefix resource IDs "
"(one IPv4 and one IPv6)."
)
for prefix_id in parsed:
if not is_valid_resource_id(prefix_id):
raise InvalidArgumentValueError(
f"'{prefix_id}' is not a valid Azure resource ID for --node-public-ip-prefix-ids."
)


def validate_nodepool_labels(namespace):
"""Validates that provided node labels is a valid format"""

Expand Down
28 changes: 28 additions & 0 deletions src/aks-preview/azext_aks_preview/agentpool_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,29 @@ def get_ip_tags(self) -> Union[List[IPTag], None]:
))
return res

def get_node_public_ip_prefix_ids(self) -> Union[List[str], None]:
"""Obtain the value of node_public_ip_prefix_ids.

Parse the comma-separated string into a list of resource IDs.

:return: list of strings or None
"""
node_public_ip_prefix_ids = self.raw_param.get("node_public_ip_prefix_ids")
if node_public_ip_prefix_ids is None:
return None
if isinstance(node_public_ip_prefix_ids, str):
parsed = [x.strip() for x in node_public_ip_prefix_ids.split(",") if x.strip()]
if not parsed:
raise InvalidArgumentValueError(
"--node-public-ip-prefix-ids must contain at least one public IP prefix resource ID."
)
return parsed
if isinstance(node_public_ip_prefix_ids, list) and not node_public_ip_prefix_ids:
raise InvalidArgumentValueError(
"--node-public-ip-prefix-ids must contain at least one public IP prefix resource ID."
)
return node_public_ip_prefix_ids

def get_node_taints(self) -> Union[List[str], None]:
"""Obtain the value of node_taints.

Expand Down Expand Up @@ -1306,6 +1329,11 @@ def set_up_agentpool_network_profile(self, agentpool: AgentPool) -> AgentPool:
if ip_tags:
agentpool.network_profile.node_public_ip_tags = ip_tags

node_public_ip_prefix_ids = self.context.get_node_public_ip_prefix_ids()
if node_public_ip_prefix_ids:
agentpool.network_profile["nodePublicIPPrefixIDs"] = node_public_ip_prefix_ids
agentpool.enable_node_public_ip = True

return agentpool

def set_up_taints(self, agentpool: AgentPool) -> AgentPool:
Expand Down
2 changes: 2 additions & 0 deletions src/aks-preview/azext_aks_preview/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,7 @@ def aks_create(
pod_ip_allocation_mode=None,
enable_node_public_ip=False,
node_public_ip_prefix_id=None,
node_public_ip_prefix_ids=None,
enable_cluster_autoscaler=False,
min_count=None,
max_count=None,
Expand Down Expand Up @@ -1908,6 +1909,7 @@ def aks_agentpool_add(
pod_ip_allocation_mode=None,
enable_node_public_ip=False,
node_public_ip_prefix_id=None,
node_public_ip_prefix_ids=None,
enable_cluster_autoscaler=False,
min_count=None,
max_count=None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,47 @@ def common_get_pod_ip_allocation_mode(self):
ctx_2.attach_agentpool(agentpool_2)
self.assertEqual(ctx_2.get_pod_ip_allocation_mode(), "StaticBlock")

def common_get_node_public_ip_prefix_ids(self):
# default - None
ctx_1 = AKSPreviewAgentPoolContext(
self.cmd,
AKSAgentPoolParamDict({"node_public_ip_prefix_ids": None}),
self.models,
DecoratorMode.CREATE,
self.agentpool_decorator_mode,
)
self.assertEqual(ctx_1.get_node_public_ip_prefix_ids(), None)

# comma-separated string
ctx_2 = AKSPreviewAgentPoolContext(
self.cmd,
AKSAgentPoolParamDict({
"node_public_ip_prefix_ids": "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/publicIPPrefixes/ipv4-prefix,"
"/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/publicIPPrefixes/ipv6-prefix"
}),
self.models,
DecoratorMode.CREATE,
self.agentpool_decorator_mode,
)
self.assertEqual(
ctx_2.get_node_public_ip_prefix_ids(),
[
"/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/publicIPPrefixes/ipv4-prefix",
"/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/publicIPPrefixes/ipv6-prefix",
],
)

# empty string should raise error
ctx_3 = AKSPreviewAgentPoolContext(
self.cmd,
AKSAgentPoolParamDict({"node_public_ip_prefix_ids": ""}),
self.models,
DecoratorMode.CREATE,
self.agentpool_decorator_mode,
)
with self.assertRaises(InvalidArgumentValueError):
ctx_3.get_node_public_ip_prefix_ids()

def common_get_skip_gpu_driver_install(self):
# default
ctx_1 = AKSPreviewAgentPoolContext(
Expand Down Expand Up @@ -1164,6 +1205,9 @@ def test_get_enable_managed_gpu(self):
def test_get_pod_ip_allocation_mode(self):
self.common_get_pod_ip_allocation_mode()

def test_get_node_public_ip_prefix_ids(self):
self.common_get_node_public_ip_prefix_ids()

def test_get_os_sku(self):
self.common_get_os_sku()

Expand Down Expand Up @@ -1264,6 +1308,9 @@ def test_get_enable_managed_gpu(self):
def test_get_pod_ip_allocation_mode(self):
self.common_get_pod_ip_allocation_mode()

def test_get_node_public_ip_prefix_ids(self):
self.common_get_node_public_ip_prefix_ids()

def test_get_os_sku(self):
self.common_get_os_sku()

Expand Down
116 changes: 116 additions & 0 deletions src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -17293,6 +17293,122 @@ def test_node_public_ip_tags(self, resource_group, resource_group_location):
],
)

# live_only: NodePublicIPv6PrefixPreview feature flag must be registered on the
# test subscription. Recording-based tests will be added once the feature is GA.
@live_only()
@AllowLargeResponse()
@AKSCustomResourceGroupPreparer(
random_name_length=17, name_prefix="clitest", location="eastus2euap"
)
def test_node_public_ip_prefix_ids(self, resource_group, resource_group_location):
aks_name = self.create_random_name("cliakstest", 16)
nodepool_name = self.create_random_name("n", 6)
nodepool_name_1 = self.create_random_name("n", 6)

self.kwargs.update(
{
"resource_group": resource_group,
"name": aks_name,
"location": resource_group_location,
"ssh_key_value": self.generate_ssh_keys(),
"node_pool_name": nodepool_name,
"node_vm_size": "standard_d2a_v4",
"ipv4_prefix": self.create_random_name("ipv4prefix", 20),
"ipv6_prefix": self.create_random_name("ipv6prefix", 20),
}
)

# Create IPv4 and IPv6 public IP prefixes
ipv4_result = self.cmd(
"network public-ip prefix create "
"--resource-group={resource_group} "
"--name={ipv4_prefix} "
"--location={location} "
"--length=28 "
"--version=IPv4"
).get_output_in_json()
ipv4_prefix_id = ipv4_result["id"]

ipv6_result = self.cmd(
"network public-ip prefix create "
"--resource-group={resource_group} "
"--name={ipv6_prefix} "
"--location={location} "
"--length=124 "
"--version=IPv6"
).get_output_in_json()
ipv6_prefix_id = ipv6_result["id"]

self.kwargs.update(
{
"prefix_ids": f"{ipv4_prefix_id},{ipv6_prefix_id}",
}
)

# Create cluster with --node-public-ip-prefix-ids
self.cmd(
"aks create "
"--resource-group={resource_group} "
"--name={name} "
"--location={location} "
"--ssh-key-value={ssh_key_value} "
"--nodepool-name={node_pool_name} "
"--node-count=1 "
"--node-vm-size={node_vm_size} "
"--node-public-ip-prefix-ids={prefix_ids} "
"--aks-custom-headers=AKSHTTPCustomFeatures=Microsoft.ContainerService/NodePublicIPv6PrefixPreview",
checks=[
self.check("provisioningState", "Succeeded"),
self.check("agentPoolProfiles[0].enableNodePublicIp", True),
self.check(
"agentPoolProfiles[0].networkProfile.nodePublicIPPrefixIDs[0]",
ipv4_prefix_id,
),
self.check(
"agentPoolProfiles[0].networkProfile.nodePublicIPPrefixIDs[1]",
ipv6_prefix_id,
),
],
)

# Add nodepool with --node-public-ip-prefix-ids
self.kwargs.update(
{
"node_pool_name": nodepool_name_1,
}
)

self.cmd(
"aks nodepool add "
"--resource-group={resource_group} "
"--cluster-name={name} "
"--name={node_pool_name} "
"--node-count=1 "
"--node-vm-size={node_vm_size} "
"--node-public-ip-prefix-ids={prefix_ids} "
"--aks-custom-headers=AKSHTTPCustomFeatures=Microsoft.ContainerService/NodePublicIPv6PrefixPreview",
checks=[
self.check("provisioningState", "Succeeded"),
self.check("enableNodePublicIp", True),
self.check(
"networkProfile.nodePublicIPPrefixIDs[0]",
ipv4_prefix_id,
),
self.check(
"networkProfile.nodePublicIPPrefixIDs[1]",
ipv6_prefix_id,
),
],
)

# delete
self.cmd(
"aks delete --resource-group={resource_group} --name={name} --yes --no-wait",
checks=[
self.is_empty(),
],
)

@AllowLargeResponse()
@AKSCustomResourceGroupPreparer(
random_name_length=17, name_prefix="clitest", location="westus2"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5959,6 +5959,7 @@ def test_set_up_agentpool_profile(self):
"pod_ip_allocation_mode": "DynamicIndividual",
"enable_node_public_ip": True,
"node_public_ip_prefix_id": "test_node_public_ip_prefix_id",
"node_public_ip_prefix_ids": None,
"enable_cluster_autoscaler": True,
"min_count": 5,
"max_count": 20,
Expand Down
2 changes: 1 addition & 1 deletion src/aks-preview/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from setuptools import find_packages, setup

VERSION = "20.0.0b4"
VERSION = "20.0.0b5"

CLASSIFIERS = [
"Development Status :: 4 - Beta",
Expand Down
Loading