Skip to content

Commit

Permalink
[AKS] Add mutable fips cli flags (enable/disable on nodepool update) (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
pineapplethevoyager committed Jun 28, 2024
1 parent 978430e commit 7c5df11
Show file tree
Hide file tree
Showing 10 changed files with 3,830 additions and 1 deletion.
4 changes: 4 additions & 0 deletions src/aks-preview/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ To release a new version, please select a new version number (usually plus 1 to
Pending
+++++++

5.0.0b3
++++++++
* Add support for mutable fips in agentpool update. (enable/disable flags)

5.0.0b2
++++++++
* Add option `--ephemeral-disk-volume-type` to `az aks create` and `az aks update` for Azure Container Storage operations.
Expand Down
6 changes: 6 additions & 0 deletions src/aks-preview/azext_aks_preview/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -2004,6 +2004,12 @@
- name: --if-none-match
type: string
short-summary: Set to '*' to allow a new node pool to be created, but to prevent updating an existing node pool. Other values will be ignored.
- name: --enable-fips-image
type: bool
short-summary: Switch to use FIPS-enabled OS on agent nodes.
- name: --disable-fips-image
type: bool
short-summary: Switch to use non-FIPS-enabled OS on agent nodes.
examples:
- name: Reconcile the nodepool back to its current state.
text: az aks nodepool update -g MyResourceGroup -n nodepool1 --cluster-name MyManagedCluster
Expand Down
10 changes: 10 additions & 0 deletions src/aks-preview/azext_aks_preview/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -1645,6 +1645,16 @@ def load_arguments(self, _):
)
c.argument("if_match")
c.argument("if_none_match")
c.argument(
"enable_fips_image",
is_preview=True,
action="store_true"
)
c.argument(
"disable_fips_image",
is_preview=True,
action="store_true"
)

with self.argument_context("aks nodepool upgrade") as c:
c.argument("max_surge", validator=validate_max_surge)
Expand Down
51 changes: 51 additions & 0 deletions src/aks-preview/azext_aks_preview/agentpool_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,39 @@ def get_vm_sizes(self) -> List[str]:
vm_sizes = [self.get_node_vm_size()]
return vm_sizes

# Overrides azure-cli command to allow changes after create
def get_enable_fips_image(self) -> bool:
"""Obtain the value of enable_fips_image, default value is False.
:return: bool
"""

# read the original value passed by the command
enable_fips_image = self.raw_param.get("enable_fips_image", False)
# In create mode, try and read the property value corresponding to the parameter from the `agentpool` object
if self.decorator_mode == DecoratorMode.CREATE:
if (
self.agentpool and
hasattr(self.agentpool, "enable_fips") and # backward compatibility
self.agentpool.enable_fips is not None
):
enable_fips_image = self.agentpool.enable_fips

# Verify both flags have not been set
if enable_fips_image and self.get_disable_fips_image():
raise MutuallyExclusiveArgumentError(
'Cannot specify "--enable-fips-image" and "--disable-fips-image" at the same time'
)

return enable_fips_image

def get_disable_fips_image(self) -> bool:
"""Obtain the value of disable_fips_image.
:return: bool
"""
# read the original value passed by the command
return self.raw_param.get("disable_fips_image")


class AKSPreviewAgentPoolAddDecorator(AKSAgentPoolAddDecorator):
def __init__(
Expand Down Expand Up @@ -1092,6 +1125,21 @@ def update_vtpm(self, agentpool: AgentPool) -> AgentPool:

return agentpool

def update_fips_image(self, agentpool: AgentPool) -> AgentPool:
"""Update fips image property for the AgentPool object.
:return: the AgentPool object
"""
self._ensure_agentpool(agentpool)

# Updates enable_fips property allowing switching of fips mode
if self.context.get_enable_fips_image():
agentpool.enable_fips = True

if self.context.get_disable_fips_image():
agentpool.enable_fips = False

return agentpool

def update_agentpool_profile_preview(self, agentpools: List[AgentPool] = None) -> AgentPool:
"""The overall controller used to update the preview AgentPool profile.
Expand Down Expand Up @@ -1121,6 +1169,9 @@ def update_agentpool_profile_preview(self, agentpools: List[AgentPool] = None) -
# update os sku
agentpool = self.update_os_sku(agentpool)

# update fips image
agentpool = self.update_fips_image(agentpool)

# update ssh access
agentpool = self.update_ssh_access(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 @@ -1387,6 +1387,8 @@ def aks_agentpool_update(
disable_vtpm=False,
if_match=None,
if_none_match=None,
enable_fips_image=False,
disable_fips_image=False,
):
# DO NOT MOVE: get all the original parameters and save them as a dictionary
raw_parameters = locals()
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,48 @@ def common_get_disable_vtpm(self):
)
ctx_1.attach_agentpool(agentpool_1)
self.assertEqual(ctx_1.get_disable_vtpm(), True)

def common_get_enable_fips_image(self):
# default
ctx_1 = AKSPreviewAgentPoolContext(
self.cmd,
AKSAgentPoolParamDict({"enable_fips_image": False}),
self.models,
DecoratorMode.CREATE,
self.agentpool_decorator_mode,
)
self.assertEqual(ctx_1.get_enable_fips_image(), False)
agentpool = self.create_initialized_agentpool_instance(enable_fips=True)
ctx_1.attach_agentpool(agentpool)
self.assertEqual(ctx_1.get_enable_fips_image(), True)

# default
ctx_2 = AKSPreviewAgentPoolContext(
self.cmd,
AKSAgentPoolParamDict({"enable_fips_image": False}),
self.models,
DecoratorMode.UPDATE,
self.agentpool_decorator_mode,
)
self.assertEqual(ctx_2.get_enable_fips_image(), False)
agentpool_2 = self.create_initialized_agentpool_instance(enable_fips=True)
ctx_2.attach_agentpool(agentpool_2)
# Update takes directly from flag value not from agentpool property
self.assertEqual(ctx_2.get_enable_fips_image(), False)

def common_get_disable_fips_image(self):
# default
ctx_1 = AKSPreviewAgentPoolContext(
self.cmd,
AKSAgentPoolParamDict({"disable_fips_image": True}),
self.models,
DecoratorMode.UPDATE,
self.agentpool_decorator_mode,
)
self.assertEqual(ctx_1.get_disable_fips_image(), True)
agentpool_1 = self.create_initialized_agentpool_instance(enable_fips=True)
ctx_1.attach_agentpool(agentpool_1)
self.assertEqual(ctx_1.get_disable_fips_image(), True)

def common_get_agentpool_windows_profile(self):
ctx_1 = AKSPreviewAgentPoolContext(
Expand Down Expand Up @@ -805,6 +847,12 @@ def test_get_enable_vtpm(self):

def test_get_disable_vtpm(self):
self.common_get_disable_vtpm()

def common_get_enable_fips_image(self):
self.common_get_enable_fips_image()

def common_get_disable_fips_image(self):
self.common_get_disable_fips_image()

def test_get_agentpool_windows_profile(self):
self.common_get_agentpool_windows_profile()
Expand Down Expand Up @@ -871,6 +919,9 @@ def test_get_disable_secure_boot(self):

def test_get_enable_vtpm(self):
self.common_get_enable_vtpm()

def common_get_enable_fips_image(self):
self.common_get_enable_fips_image()

def test_get_agentpool_windows_profile(self):
self.common_get_agentpool_windows_profile()
Expand Down Expand Up @@ -1681,6 +1732,53 @@ def common_update_vtpm(self):
with self.assertRaises(MutuallyExclusiveArgumentError):
dec_3.update_vtpm(agentpool_2)

def common_update_fips_image(self):
dec_1 = AKSPreviewAgentPoolUpdateDecorator(
self.cmd,
self.client,
{"enable_fips_image": True, "disable_fips_image": False},
self.resource_type,
self.agentpool_decorator_mode,
)
# fail on passing the wrong agentpool object
with self.assertRaises(CLIInternalError):
dec_1.update_fips_image(None)

agentpool_1 = self.create_initialized_agentpool_instance(enable_fips=False)
dec_1.context.attach_agentpool(agentpool_1)
dec_agentpool_1 = dec_1.update_fips_image(agentpool_1)
ground_truth_agentpool_1 = self.create_initialized_agentpool_instance(enable_fips=True)
self.assertEqual(dec_agentpool_1, ground_truth_agentpool_1)

dec_2 = AKSPreviewAgentPoolUpdateDecorator(
self.cmd,
self.client,
{"enable_fips_image": False, "disable_fips_image": True},
self.resource_type,
self.agentpool_decorator_mode,
)
# fail on passing the wrong agentpool object
with self.assertRaises(CLIInternalError):
dec_2.update_fips_image(None)

agentpool_2 = self.create_initialized_agentpool_instance(enable_fips=True)
dec_2.context.attach_agentpool(agentpool_2)
dec_agentpool_2 = dec_2.update_fips_image(agentpool_2)
ground_truth_agentpool_2 = self.create_initialized_agentpool_instance(enable_fips=False)
self.assertEqual(dec_agentpool_2, ground_truth_agentpool_2)

# Should error if both set
dec_3 = AKSPreviewAgentPoolUpdateDecorator(
self.cmd,
self.client,
{"enable_fips_image": True, "disable_fips_image": True},
self.resource_type,
self.agentpool_decorator_mode,
)
dec_3.context.attach_agentpool(agentpool_2)
with self.assertRaises(MutuallyExclusiveArgumentError):
dec_3.update_fips_image(agentpool_2)


class AKSPreviewAgentPoolUpdateDecoratorStandaloneModeTestCase(
AKSPreviewAgentPoolUpdateDecoratorCommonTestCase
Expand Down Expand Up @@ -1709,6 +1807,9 @@ def test_update_secure_boot(self):
def test_update_vtpm(self):
self.common_update_vtpm()

def test_update_fips_image(self):
self.common_update_fips_image()

def test_update_agentpool_profile_preview(self):
import inspect

Expand Down Expand Up @@ -1787,6 +1888,9 @@ def test_update_secure_boot(self):
def test_update_vtpm(self):
self.common_update_vtpm()

def test_update_fips_image(self):
self.common_update_fips_image()

def test_update_agentpool_profile_preview(self):
import inspect

Expand Down
103 changes: 103 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 @@ -4645,6 +4645,109 @@ def test_aks_create_update_vtpm_flow(self, resource_group, resource_group_locati
checks=[self.is_empty()],
)

@AllowLargeResponse()
@AKSCustomResourceGroupPreparer(
random_name_length=17, name_prefix="clitest", location="eastus2euap"
)
def test_aks_create_update_fips_flow(self, resource_group, resource_group_location):
# reset the count so in replay mode the random names will start with 0
self.test_resources_count = 0
aks_name = self.create_random_name("cliakstest", 16)
node_pool_name = self.create_random_name("c", 6)
node_pool_name_second = self.create_random_name("c", 6)
self.kwargs.update(
{
"resource_group": resource_group,
"name": aks_name,
"dns_name_prefix": self.create_random_name("cliaksdns", 16),
"location": resource_group_location,
"resource_type": "Microsoft.ContainerService/ManagedClusters",
"node_pool_name": node_pool_name,
"node_pool_name_second": node_pool_name_second,
"ssh_key_value": self.generate_ssh_keys(),
}
)

# 1. create
create_cmd = (
"aks create --resource-group={resource_group} --name={name} --location={location} "
"--nodepool-name {node_pool_name} -c 1 --enable-managed-identity "
"--ssh-key-value={ssh_key_value} "
'--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/MutableFipsPreview '
"--enable-fips-image"
)
self.cmd(
create_cmd,
checks=[
self.check("provisioningState", "Succeeded"),
self.check("agentPoolProfiles[0].enableFips", True),
],
)

# verify same update no change
self.cmd(
"aks nodepool update --resource-group={resource_group} --cluster-name={name} --name={node_pool_name} "
'--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/MutableFipsPreview '
"--enable-fips-image",
checks=[
self.check("provisioningState", "Succeeded"),
self.check("enableFips", True),
],
)

# update nodepool1 to disable
self.cmd(
"aks nodepool update --resource-group={resource_group} --cluster-name={name} --name={node_pool_name} "
'--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/MutableFipsPreview '
"--disable-fips-image",
checks=[
self.check("provisioningState", "Succeeded"),
self.check("enableFips", False),
],
)

# 2. add nodepool2
self.cmd(
"aks nodepool add "
"--resource-group={resource_group} "
"--cluster-name={name} "
"--name={node_pool_name_second} "
"--os-type Linux "
'--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/MutableFipsPreview ',
checks=[
self.check("provisioningState", "Succeeded"),
self.check("enableFips", False),
],
)

# verify same update no change
self.cmd(
"aks nodepool update --resource-group={resource_group} --cluster-name={name} --name={node_pool_name_second} "
'--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/MutableFipsPreview '
"--disable-fips-image",
checks=[
self.check("provisioningState", "Succeeded"),
self.check("enableFips", False),
],
)

# update nodepool2 to enable
self.cmd(
"aks nodepool update --resource-group={resource_group} --cluster-name={name} --name={node_pool_name_second} "
'--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/MutableFipsPreview '
"--enable-fips-image",
checks=[
self.check("provisioningState", "Succeeded"),
self.check("enableFips", True),
],
)

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

@AllowLargeResponse()
@AKSCustomResourceGroupPreparer(
random_name_length=17, name_prefix="clitest", location="westus2"
Expand Down
6 changes: 6 additions & 0 deletions src/aks-preview/linter_exclusions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,12 @@ aks nodepool update:
disable_vtpm:
rule_exclusions:
- option_length_too_long
enable_fips_image:
rule_exclusions:
- option_length_too_long
disable_fips_image:
rule_exclusions:
- option_length_too_long
aks nodepool delete:
parameters:
ignore_pod_disruption_budget:
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 setup, find_packages

VERSION = "5.0.0b2"
VERSION = "5.0.0b3"

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

0 comments on commit 7c5df11

Please sign in to comment.