Skip to content

Commit e06e58a

Browse files
author
Mikolaj Umanski
committed
feat: add motd to GA CLI
1 parent ff23121 commit e06e58a

File tree

7 files changed

+132
-2
lines changed

7 files changed

+132
-2
lines changed

src/azure-cli/azure/cli/command_modules/acs/_help.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,9 @@
265265
type: string
266266
short-summary: A CIDR notation IP range from which to assign pod IPs when kubenet is used.
267267
long-summary: This range must not overlap with any Subnet IP ranges. For example, 172.244.0.0/16.
268+
- name: --message-of-the-day
269+
type: string
270+
short-summary: Path to a file containing the desired message of the day. Only valid for linux nodes. Will be written to /etc/motd.
268271
- name: --service-cidr
269272
type: string
270273
short-summary: A CIDR notation IP range from which to assign service cluster IPs.
@@ -1608,6 +1611,9 @@
16081611
- name: --linux-os-config
16091612
type: string
16101613
short-summary: Path to JSON file containing OS configurations for Linux agent nodes. https://aka.ms/aks/custom-node-config
1614+
- name: --message-of-the-day
1615+
type: string
1616+
short-summary: Path to a file containing the desired message of the day. Only valid for linux nodes. Will be written to /etc/motd.
16111617
- name: --host-group-id
16121618
type: string
16131619
short-summary: The fully qualified dedicated host group id used to provision agent node pool.

src/azure-cli/azure/cli/command_modules/acs/_params.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@
9999
validate_node_public_ip_tags,
100100
validate_disable_windows_outbound_nat,
101101
validate_crg_id,
102-
validate_azure_service_mesh_revision)
102+
validate_azure_service_mesh_revision,
103+
validate_message_of_the_day)
103104
from azure.cli.core.commands.parameters import (
104105
edge_zone_type, file_type, get_enum_type,
105106
get_resource_name_completion_list, get_three_state_flag, name_type,
@@ -424,6 +425,8 @@ def load_arguments(self, _):
424425
c.argument('gpu_instance_profile', arg_type=get_enum_type(gpu_instance_profiles))
425426
c.argument('nodepool_allowed_host_ports', nargs='+', validator=validate_allowed_host_ports, help="allowed host ports for agentpool")
426427
c.argument('nodepool_asg_ids', nargs='+', validator=validate_application_security_groups, help="application security groups for agentpool")
428+
c.argument("message_of_the_day")
429+
427430
# azure monitor profile
428431
c.argument('enable_azure_monitor_metrics', action='store_true')
429432
c.argument('azure_monitor_workspace_resource_id', validator=validate_azuremonitorworkspaceresourceid)
@@ -791,6 +794,7 @@ def load_arguments(self, _):
791794
c.argument('asg_ids', nargs='+', validator=validate_application_security_groups)
792795
c.argument('node_public_ip_tags', arg_type=tags_type, validator=validate_node_public_ip_tags,
793796
help='space-separated tags: key[=value] [key[=value] ...].')
797+
c.argument("message_of_the_day", validator=validate_message_of_the_day)
794798
c.argument('enable_vtpm', action='store_true')
795799
c.argument('enable_secure_boot', action='store_true')
796800
c.argument("if_match")

src/azure-cli/azure/cli/command_modules/acs/_validators.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,3 +827,10 @@ def validate_disable_windows_outbound_nat(namespace):
827827
if hasattr(namespace, 'os_type') and str(namespace.os_type).lower() != "windows":
828828
raise ArgumentUsageError(
829829
'--disable-windows-outbound-nat can only be set for Windows nodepools')
830+
831+
def validate_message_of_the_day(namespace):
832+
"""Validates message of the day can only be used on Linux."""
833+
if namespace.message_of_the_day is not None and namespace.message_of_the_day != "":
834+
if namespace.os_type is not None and namespace.os_type != "Linux":
835+
raise ArgumentUsageError(
836+
'--message-of-the-day can only be set for linux nodepools')

src/azure-cli/azure/cli/command_modules/acs/agentpool_decorator.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# --------------------------------------------------------------------------------------------
55

66
import os
7+
import base64
78
from math import isnan
89
from types import SimpleNamespace
910
from typing import Dict, List, Tuple, TypeVar, Union
@@ -41,7 +42,7 @@
4142
from azure.cli.core.cloud import get_active_cloud
4243
from azure.cli.core.commands import AzCliCommand
4344
from azure.cli.core.profiles import ResourceType
44-
from azure.cli.core.util import get_file_json, sdk_no_wait
45+
from azure.cli.core.util import get_file_json, sdk_no_wait, read_file_content
4546
from knack.log import get_logger
4647

4748
logger = get_logger(__name__)
@@ -390,6 +391,33 @@ def _get_crg_id(self) -> Union[str, None]:
390391
crg_id = raw_value
391392
return crg_id
392393

394+
def get_message_of_the_day(self) -> Union[str, None]:
395+
"""Obtain the value of message_of_the_day.
396+
397+
:return: string or None
398+
"""
399+
# read the original value passed by the command
400+
message_of_the_day = None
401+
message_of_the_day_file_path = self.raw_param.get("message_of_the_day")
402+
403+
if message_of_the_day_file_path:
404+
if not os.path.isfile(message_of_the_day_file_path):
405+
raise InvalidArgumentValueError(
406+
f"{message_of_the_day_file_path} is not valid file, or not accessible."
407+
)
408+
message_of_the_day = read_file_content(
409+
message_of_the_day_file_path)
410+
message_of_the_day = base64.b64encode(
411+
bytes(message_of_the_day, 'ascii')).decode('ascii')
412+
413+
# try to read the property value corresponding to the parameter from the `mc` object
414+
if self.agentpool and self.agentpool.message_of_the_day is not None:
415+
message_of_the_day = self.agentpool.message_of_the_day
416+
417+
# this parameter does not need dynamic completion
418+
# this parameter does not need validation
419+
return message_of_the_day
420+
393421
def get_enable_vtpm(self) -> bool:
394422
return self._get_enable_vtpm(enable_validation=True)
395423

@@ -1797,6 +1825,16 @@ def set_up_custom_node_config(self, agentpool: AgentPool) -> AgentPool:
17971825
agentpool.linux_os_config = self.context.get_linux_os_config()
17981826
return agentpool
17991827

1828+
def set_up_motd(self, agentpool: AgentPool) -> AgentPool:
1829+
"""Set up message of the day for the AgentPool object.
1830+
1831+
:return: the AgentPool object
1832+
"""
1833+
self._ensure_agentpool(agentpool)
1834+
1835+
agentpool.message_of_the_day = self.context.get_message_of_the_day()
1836+
return agentpool
1837+
18001838
def set_up_gpu_properties(self, agentpool: AgentPool) -> AgentPool:
18011839
"""Set up gpu related properties for the AgentPool object.
18021840

src/azure-cli/azure/cli/command_modules/acs/custom.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,7 @@ def aks_create(
629629
host_group_id=None,
630630
crg_id=None,
631631
gpu_instance_profile=None,
632+
message_of_the_day=None,
632633
# azure service mesh
633634
enable_azure_service_mesh=None,
634635
revision=None,
@@ -2385,6 +2386,7 @@ def aks_agentpool_add(
23852386
labels=None,
23862387
tags=None,
23872388
node_taints=None,
2389+
message_of_the_day=None,
23882390
node_osdisk_type=None,
23892391
node_osdisk_size=None,
23902392
max_surge=None,

src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_agentpool_decorator.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,39 @@ def common_get_scale_down_mode(self):
13141314
ctx_2.attach_agentpool(agentpool_2)
13151315
self.assertEqual(ctx_2.get_scale_down_mode(), "test_scale_down_mode")
13161316

1317+
def common_get_message_of_the_day(self):
1318+
# default
1319+
ctx_1 = AKSAgentPoolContext(
1320+
self.cmd,
1321+
AKSAgentPoolParamDict({"message_of_the_day": None}),
1322+
self.models,
1323+
DecoratorMode.CREATE,
1324+
self.agentpool_decorator_mode,
1325+
)
1326+
self.assertEqual(ctx_1.get_message_of_the_day(), None)
1327+
agent_pool_profile = self.models.ManagedClusterAgentPoolProfile(
1328+
name="test_nodepool_name",
1329+
message_of_the_day="test_mc_message_of_the_day",
1330+
)
1331+
# mc = self.models.ManagedCluster(
1332+
# location="test_location", agent_pool_profiles=[agent_pool_profile]
1333+
# )
1334+
ctx_1.attach_agentpool(agent_pool_profile)
1335+
self.assertEqual(
1336+
ctx_1.get_message_of_the_day(), "test_mc_message_of_the_day"
1337+
)
1338+
1339+
ctx_2 = AKSAgentPoolContext(
1340+
self.cmd,
1341+
AKSAgentPoolParamDict({"message_of_the_day": "fake-path"}),
1342+
self.models,
1343+
DecoratorMode.CREATE,
1344+
self.agentpool_decorator_mode,
1345+
)
1346+
# fail on invalid file path
1347+
with self.assertRaises(InvalidArgumentValueError):
1348+
ctx_2.get_message_of_the_day()
1349+
13171350
def common_get_kubelet_config(self):
13181351
# default
13191352
ctx_1 = AKSAgentPoolContext(
@@ -1718,6 +1751,9 @@ def test_get_mode(self):
17181751
def test_get_scale_down_mode(self):
17191752
self.common_get_scale_down_mode()
17201753

1754+
def test_get_message_of_the_day(self):
1755+
self.common_get_message_of_the_day()
1756+
17211757
def test_get_kubelet_config(self):
17221758
self.common_get_kubelet_config()
17231759

@@ -1893,6 +1929,9 @@ def test_get_mode(self):
18931929
def test_get_scale_down_mode(self):
18941930
self.common_get_scale_down_mode()
18951931

1932+
def test_get_message_of_the_day(self):
1933+
self.common_get_message_of_the_day()
1934+
18961935
def test_get_kubelet_config(self):
18971936
self.common_get_kubelet_config()
18981937

src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_validators.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,40 @@ def test_throws_on_negative(self):
164164
self.assertTrue('positive' in str(cm.exception), msg=str(cm.exception))
165165

166166

167+
class MessageOfTheDayNamespace:
168+
def __init__(self, message_of_the_day, os_type):
169+
self.os_type = os_type
170+
self.message_of_the_day = message_of_the_day
171+
172+
class TestMessageOfTheday(unittest.TestCase):
173+
def test_valid_cases(self):
174+
valid = ["foo", ""]
175+
for v in valid:
176+
validators.validate_message_of_the_day(MessageOfTheDayNamespace(v, "Linux"))
177+
178+
def test_fail_if_os_type_windows(self):
179+
with self.assertRaises(CLIError) as cm:
180+
validators.validate_message_of_the_day(
181+
MessageOfTheDayNamespace("foo", "Windows")
182+
)
183+
self.assertTrue(
184+
"--message-of-the-day can only be set for linux nodepools"
185+
in str(cm.exception),
186+
msg=str(cm.exception),
187+
)
188+
189+
def test_fail_if_os_type_invalid(self):
190+
with self.assertRaises(CLIError) as cm:
191+
validators.validate_message_of_the_day(
192+
MessageOfTheDayNamespace("foo", "invalid")
193+
)
194+
self.assertTrue(
195+
"--message-of-the-day can only be set for linux nodepools"
196+
in str(cm.exception),
197+
msg=str(cm.exception),
198+
)
199+
200+
167201
class TestLabels(unittest.TestCase):
168202
def test_invalid_labels_prefix(self):
169203
invalid_labels = "k8s##.io/label1=value"

0 commit comments

Comments
 (0)