Skip to content
Merged
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
10 changes: 10 additions & 0 deletions src/fleet/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,13 @@ Release History
0.2.7
++++++
* Fix for `az fleet updaterun --node-image-selection` argument.

0.2.8
++++++
* Updates to Fleet identity options.

0.3.0
++++++
* Resolved issues related to system & user assigned MSI.
* UpdateRun now takes a Strategy name in lieu of resource Id, e.g., `az fleet updaterun create --update_strategy_name UpdateStrategyName`
* Deletes now require confirmation.
20 changes: 13 additions & 7 deletions src/fleet/azext_fleet/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,28 @@
type: command
short-summary: Creates or updates a Fleet.
parameters:
- name: --tags
type: string
short-summary: The tags of the managed cluster. The managed cluster instance and all resources managed by the cloud provider will be tagged.
- name: --dns-name-prefix -p
type: string
short-summary: Prefix for hostnames that are created. If not specified, generate a hostname using the
managed cluster and resource group names.
examples:
- name: Create a hubless fleet
text: az fleet create -g MyResourceGroup -l MyLocation -n MyFleetName --tags "TagKey=TagValue"
- name: Create a hubful fleet
text: az fleet create -g MyResourceGroup -l MyLocation -n MyFleetName --enable-hub --tags "TagKey=TagValue"

"""

helps['fleet update'] = """
type: command
short-summary: Patches a fleet resource.
parameters:
- name: --tags
type: string
short-summary: The tags of the managed cluster. The managed cluster instance and all resources managed by the cloud provider will be tagged.
examples:
- name: Update a Fleet's tags
text: az fleet update -g MyResourceGroup -n MyFleetName --tags Key=Value
- name: Update a Fleet to use a system assigned managed service identity.
text: az fleet update -g MyResourceGroup -n MyFleetName --enable-managed-identity --tags Key=Value
- name: Update a Fleet to use a user assigned managed service identity.
text: az fleet update -g MyResourceGroup -n MyFleetName --enable-managed-identity --assign-identity "/subscription/00000000-0000-0000-0000-000000000000/resourcegroup/MyResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/MyIdentity" --tags Key=Value
"""

helps['fleet show'] = """
Expand Down
9 changes: 5 additions & 4 deletions src/fleet/azext_fleet/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
tags_type,
file_type,
get_location_type,
get_enum_type
get_enum_type,
get_three_state_flag
)
from azure.cli.core.commands.validators import get_default_location_from_resource_group
from azext_fleet._validators import validate_member_cluster_id, validate_kubernetes_version, validate_apiserver_subnet_id, validate_agent_subnet_id, validate_assign_identity, validate_update_strategy_id
from azext_fleet._validators import validate_member_cluster_id, validate_kubernetes_version, validate_apiserver_subnet_id, validate_agent_subnet_id, validate_assign_identity, validate_update_strategy_name


def load_arguments(self, _):
Expand All @@ -34,7 +35,7 @@ def load_arguments(self, _):

with self.argument_context('fleet update') as c:
c.argument('tags', tags_type)
c.argument('enable_managed_identity', action='store_true', is_preview=True, help='Enable system assigned managed identity (MSI) on the Fleet resource.')
c.argument('enable_managed_identity', arg_type=get_three_state_flag(), is_preview=True, help='Enable system assigned managed identity (MSI) on the Fleet resource.')
c.argument('assign_identity', validator=validate_assign_identity, is_preview=True, help='With --enable-managed-identity, enable user assigned managed identity (MSI) on the Fleet resource. Specify the existing user assigned identity resource.')

with self.argument_context('fleet get-credentials') as c:
Expand All @@ -61,7 +62,7 @@ def load_arguments(self, _):
c.argument('kubernetes_version', validator=validate_kubernetes_version)
c.argument('node_image_selection', arg_type=get_enum_type(['Latest', 'Consistent']), help='Node Image Selection is an option that lets you choose how your clusters\' nodes are upgraded')
c.argument('stages', type=file_type, completer=FilesCompleter(), help='Path to a json file that defines stages to upgrade a fleet. See examples for further reference.')
c.argument('update_strategy_id', validator=validate_update_strategy_id, help='The ID of the update strategy to use for this update run. If not specified, the default update strategy will be used.')
c.argument('update_strategy_name', validator=validate_update_strategy_name, help='The name of the update strategy to use for this update run. If not specified, the default update strategy will be used.')

with self.argument_context('fleet updatestrategy') as c:
c.argument('name', options_list=['--name', '-n'], help='Specify name for the fleet update strategy.')
Expand Down
23 changes: 10 additions & 13 deletions src/fleet/azext_fleet/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

import semver
import re

from azure.cli.core.azclierror import InvalidArgumentValueError
from azure.cli.core.util import CLIError
Expand All @@ -18,12 +18,12 @@ def validate_member_cluster_id(namespace):


def validate_kubernetes_version(namespace):
try:
if namespace.kubernetes_version:
semver.VersionInfo.parse(namespace.kubernetes_version)
except ValueError:
raise InvalidArgumentValueError(
"--kubernetes-version must be set as version x.x.x (eg. 1.2.3)")
if namespace.kubernetes_version:
k8s_release_regex = re.compile(r'^[v|V]?(\d+\.\d+(?:\.\d+)?)$')
found = k8s_release_regex.findall(namespace.kubernetes_version)
if not found:
raise InvalidArgumentValueError(
'--kubernetes-version should be the full version number or alias minor version, such as "1.7.12" or "1.7"')


def validate_apiserver_subnet_id(namespace):
Expand All @@ -34,12 +34,9 @@ def validate_agent_subnet_id(namespace):
_validate_subnet_id(namespace.agent_subnet_id, "--agent-subnet-id")


def validate_update_strategy_id(namespace):
if namespace.update_strategy_id is not None:
from msrestazure.tools import is_valid_resource_id
if not is_valid_resource_id(namespace.update_strategy_id):
raise CLIError(
"--update-strategy-id is not a valid Azure resource ID.")
def validate_update_strategy_name(namespace):
if namespace.update_strategy_name is not None and not namespace.update_strategy_name.strip():
raise CLIError("--update-strategy-name is not a valid name")


def _validate_subnet_id(subnet_id, name):
Expand Down
8 changes: 4 additions & 4 deletions src/fleet/azext_fleet/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ def load_command_table(self, _):
g.custom_command("update", "update_fleet", supports_no_wait=True)
g.custom_show_command("show", "show_fleet")
g.custom_command("list", "list_fleet")
g.custom_command("delete", "delete_fleet", supports_no_wait=True)
g.custom_command("delete", "delete_fleet", supports_no_wait=True, confirmation=True)
g.custom_command("get-credentials", "get_credentials")
g.wait_command("wait")

# fleet members command group
with self.command_group("fleet member", fleet_members_sdk, client_factory=cf_fleet_members) as g:
g.custom_command("create", "create_fleet_member", supports_no_wait=True)
g.custom_command("update", "update_fleet_member")
g.custom_command("delete", "delete_fleet_member", supports_no_wait=True)
g.custom_command("delete", "delete_fleet_member", supports_no_wait=True, confirmation=True)
g.custom_command("list", "list_fleet_member")
g.custom_show_command("show", "show_fleet_member")
g.wait_command("wait")
Expand All @@ -58,7 +58,7 @@ def load_command_table(self, _):
g.custom_command("create", "create_update_run", supports_no_wait=True)
g.custom_show_command("show", "show_update_run")
g.custom_command("list", "list_update_run")
g.custom_command("delete", "delete_update_run", supports_no_wait=True)
g.custom_command("delete", "delete_update_run", supports_no_wait=True, confirmation=True)
g.custom_command("start", "start_update_run", supports_no_wait=True)
g.custom_command("stop", "stop_update_run", supports_no_wait=True)
g.wait_command("wait")
Expand All @@ -68,5 +68,5 @@ def load_command_table(self, _):
g.custom_command("create", "create_fleet_update_strategy", supports_no_wait=True)
g.custom_show_command("show", "show_fleet_update_strategy")
g.custom_command("list", "list_fleet_update_strategies")
g.custom_command("delete", "delete_fleet_update_strategy", supports_no_wait=True)
g.custom_command("delete", "delete_fleet_update_strategy", supports_no_wait=True, confirmation=True)
g.wait_command("wait")
72 changes: 56 additions & 16 deletions src/fleet/azext_fleet/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from azext_fleet._helpers import print_or_merge_credentials


# pylint: disable=too-many-locals
def create_fleet(cmd,
client,
resource_group_name,
Expand All @@ -38,7 +39,9 @@ def create_fleet(cmd,
operation_group="fleets"
)

poll_interval = 5
if enable_hub:
poll_interval = 30
fleet_hub_profile_model = cmd.get_models(
"FleetHubProfile",
resource_type=CUSTOM_MGMT_FLEET,
Expand Down Expand Up @@ -94,8 +97,13 @@ def create_fleet(cmd,
if enable_managed_identity:
managed_service_identity.type = "SystemAssigned"
if assign_identity is not None:
user_assigned_identity_model = cmd.get_models(
"UserAssignedIdentity",
resource_type=CUSTOM_MGMT_FLEET,
operation_group="fleets"
)
managed_service_identity.type = "UserAssigned"
managed_service_identity.user_assigned_identities = {assign_identity: None}
managed_service_identity.user_assigned_identities = {assign_identity: user_assigned_identity_model()}
elif assign_identity is not None:
raise CLIError("Cannot assign identity without enabling managed identity.")

Expand All @@ -106,14 +114,19 @@ def create_fleet(cmd,
identity=managed_service_identity
)

return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, name, fleet)
return sdk_no_wait(no_wait,
client.begin_create_or_update,
resource_group_name,
name,
fleet,
polling_interval=poll_interval)


def update_fleet(cmd,
client,
resource_group_name,
name,
enable_managed_identity=False,
enable_managed_identity=None,
assign_identity=None,
tags=None,
no_wait=False):
Expand All @@ -128,20 +141,29 @@ def update_fleet(cmd,
operation_group="fleets"
)

managed_service_identity = fleet_managed_service_identity_model(type="None")
if enable_managed_identity:
managed_service_identity.type = "SystemAssigned"
if enable_managed_identity is None:
managed_service_identity = None
if assign_identity is not None:
raise CLIError("Cannot assign identity without enabling managed identity.")
elif enable_managed_identity is False:
managed_service_identity = fleet_managed_service_identity_model(type="None")
else:
managed_service_identity = fleet_managed_service_identity_model(type="SystemAssigned")
if assign_identity is not None:
user_assigned_identity_model = cmd.get_models(
"UserAssignedIdentity",
resource_type=CUSTOM_MGMT_FLEET,
operation_group="fleets"
)
managed_service_identity.type = "UserAssigned"
managed_service_identity.user_assigned_identities = {assign_identity: None}
managed_service_identity.user_assigned_identities = {assign_identity: user_assigned_identity_model()}

fleet_patch = fleet_patch_model(
tags=tags,
identity=managed_service_identity
)

fleet_patch = fleet_patch_model(tags=tags)
return sdk_no_wait(no_wait, client.begin_update, resource_group_name, name, fleet_patch)
return sdk_no_wait(no_wait, client.begin_update, resource_group_name, name, fleet_patch, polling_interval=5)


def show_fleet(cmd, # pylint: disable=unused-argument
Expand All @@ -164,7 +186,7 @@ def delete_fleet(cmd, # pylint: disable=unused-argument
resource_group_name,
name,
no_wait=False):
return sdk_no_wait(no_wait, client.begin_delete, resource_group_name, name)
return sdk_no_wait(no_wait, client.begin_delete, resource_group_name, name, polling_interval=5)


def get_credentials(cmd, # pylint: disable=unused-argument
Expand Down Expand Up @@ -251,17 +273,17 @@ def create_update_run(cmd,
fleet_name,
name,
upgrade_type,
node_image_selection,
node_image_selection=None,
kubernetes_version=None,
stages=None,
update_strategy_id=None,
update_strategy_name=None,
no_wait=False):
if upgrade_type == "Full" and kubernetes_version is None:
raise CLIError("Please set kubernetes version when upgrade type is 'Full'.")
if upgrade_type == "NodeImageOnly" and kubernetes_version is not None:
raise CLIError("Cannot set kubernetes version when upgrade type is 'NodeImageOnly'.")
if stages is not None and update_strategy_id is not None:
raise CLIError("Cannot set stages when update strategy id is set.")
if stages is not None and update_strategy_name is not None:
raise CLIError("Cannot set stages when update strategy name is set.")

update_run_strategy = get_update_run_strategy(cmd, "update_runs", stages)

Expand All @@ -288,18 +310,36 @@ def create_update_run(cmd,

managed_cluster_upgrade_spec = managed_cluster_upgrade_spec_model(
type=upgrade_type, kubernetes_version=kubernetes_version)
if node_image_selection is None:
node_image_selection = "Latest"
node_image_selection_type = node_image_selection_model(type=node_image_selection)

managed_cluster_update = managed_cluster_update_model(
upgrade=managed_cluster_upgrade_spec,
node_image_selection=node_image_selection_type)

updateStrategyId = None
if update_strategy_name is not None:
subId = get_subscription_id(cmd.cli_ctx)
updateStrategyId = (
f"/subscriptions/{subId}/resourceGroups/{resource_group_name}"
f"/providers/Microsoft.ContainerService/fleets/{fleet_name}/updateStrategies/{update_strategy_name}"
)

update_run = update_run_model(
update_strategy_id=update_strategy_id,
update_strategy_id=updateStrategyId,
strategy=update_run_strategy,
managed_cluster_update=managed_cluster_update)

return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, fleet_name, name, update_run)
result = None
try:
result = sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, fleet_name, name, update_run)
print("After successfully creating the run, you need to use the following command to start the run:"
f"az fleet updaterun start --resource-group={resource_group_name} --fleet={fleet_name} --name={name}")
except Exception as e:
return e

return result


def show_update_run(cmd, # pylint: disable=unused-argument
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,6 @@ def test_fleet_hubful(self):

self.cmd('fleet member wait -g {rg} --fleet-name {fleet_name} --fleet-member-name {member_name} --created', checks=[self.is_empty()])

self.cmd('fleet member delete -g {rg} --fleet-name {fleet_name} -n {member_name}')
self.cmd('fleet member delete -g {rg} --fleet-name {fleet_name} -n {member_name} --yes')

self.cmd('fleet delete -g {rg} -n {fleet_name}')
self.cmd('fleet delete -g {rg} -n {fleet_name} --yes')
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ def test_fleet_hubless(self):
self.check('name', '{updaterun}')
])

self.cmd('fleet updaterun delete -g {rg} -n {updaterun} -f {fleet_name}')
self.cmd('fleet updaterun delete -g {rg} -n {updaterun} -f {fleet_name} --yes')

update_strategy_id = self.cmd('fleet updatestrategy create -g {rg} -n {updateStrategy_name} -f {fleet_name} --stages {stages_file}', checks=[
update_strategy_name = self.cmd('fleet updatestrategy create -g {rg} -n {updateStrategy_name} -f {fleet_name} --stages {stages_file}', checks=[
self.check('name', '{updateStrategy_name}')
]).get_output_in_json()['id']

Expand All @@ -122,10 +122,10 @@ def test_fleet_hubless(self):
])

self.kwargs.update({
'update_strategy_id': update_strategy_id,
'update_strategy_name': update_strategy_name,
})

self.cmd('fleet updaterun create -g {rg} -n {updaterun} -f {fleet_name} --upgrade-type Full --node-image-selection Latest --kubernetes-version 1.27.1 --update-strategy-id {update_strategy_id}', checks=[
self.cmd('fleet updaterun create -g {rg} -n {updaterun} -f {fleet_name} --upgrade-type Full --node-image-selection Latest --kubernetes-version 1.27.1 --update-strategy-name {update_strategy_name}', checks=[
self.check('name', '{updaterun}')
])

Expand All @@ -141,10 +141,10 @@ def test_fleet_hubless(self):
self.check('length([])', 1)
])

self.cmd('fleet updaterun delete -g {rg} -n {updaterun} -f {fleet_name}')
self.cmd('fleet updaterun delete -g {rg} -n {updaterun} -f {fleet_name} --yes')

self.cmd('fleet updatestrategy delete -g {rg} -f {fleet_name} -n {updateStrategy_name}')
self.cmd('fleet updatestrategy delete -g {rg} -f {fleet_name} -n {updateStrategy_name} --yes')

self.cmd('fleet member delete -g {rg} --fleet-name {fleet_name} -n {member_name}')
self.cmd('fleet member delete -g {rg} --fleet-name {fleet_name} -n {member_name} --yes')

self.cmd('fleet delete -g {rg} -n {fleet_name}')
self.cmd('fleet delete -g {rg} -n {fleet_name} --yes')
Loading