Skip to content

Commit

Permalink
[AKS] Get versions + get upgrades + add "preview" suffix after previe…
Browse files Browse the repository at this point in the history
…w k8s version (#671)
  • Loading branch information
zqingqing1 authored and yugangw-msft committed May 6, 2019
1 parent 267d086 commit aa0b237
Show file tree
Hide file tree
Showing 31 changed files with 716 additions and 30 deletions.
6 changes: 6 additions & 0 deletions src/aks-preview/HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

Release History
===============
0.4.1
+++++
* Add `az aks get-versions -l location` to allow users to see all managed cluster versions.
* Add `az aks get-upgrades` to get all available versions to upgrade.
* Add '(preview)' suffix if kubernetes version is preview when using `get-versions` and `get-upgrades`

0.4.0
+++++
* Add support for Azure policy add-on.
Expand Down
2 changes: 1 addition & 1 deletion src/aks-preview/azext_aks_preview/_client_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def cf_container_services(cli_ctx, *_):


def get_container_service_client(cli_ctx, **_):
return get_mgmt_service_client(cli_ctx, CUSTOM_MGMT_AKS)
return get_mgmt_service_client(cli_ctx, CUSTOM_MGMT_AKS_PREVIEW)


def cf_managed_clusters(cli_ctx, *_):
Expand Down
62 changes: 54 additions & 8 deletions src/aks-preview/azext_aks_preview/_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,38 +58,55 @@ def aks_upgrades_table_format(result):
# pylint: disable=import-error
from jmespath import compile as compile_jmes, Options

preview = []
for i, j in result.items():
if i == "controlPlaneProfile":
# the reason why we choose "upgrades" obejct, since only it has the isPreview boolean.
for item in j["upgrades"]:
if item["isPreview"]:
preview.append(item["kubernetesVersion"])
# This expression assumes there is one node pool, and that the master and nodes upgrade in lockstep.
parsed = compile_jmes("""{
name: name,
resourceGroup: resourceGroup,
masterVersion: controlPlaneProfile.kubernetesVersion || `unknown`,
nodePoolVersion: agentPoolProfiles[0].kubernetesVersion || `unknown`,
upgrades: controlPlaneProfile.upgrades || [`None available`] | sort_versions(@) | join(`, `, @)
masterVersion: controlPlaneProfile.kubernetesVersion || `unknown` | set_preview(@),
nodePoolVersion: agentPoolProfiles[0].kubernetesVersion || `unknown` | set_preview(@),
upgrades: controlPlaneProfile.upgrades[].kubernetesVersion || [`None available`] | sort_versions(@) | set_preview_array(@) | join(`, `, @)
}""")
# use ordered dicts so headers are predictable
return parsed.search(result, Options(dict_cls=OrderedDict, custom_functions=_custom_functions()))
return parsed.search(result, Options(dict_cls=OrderedDict, custom_functions=_custom_functions(preview)))


def aks_versions_table_format(result):
"""Format get-versions results as a summary for display with "-o table"."""
# pylint: disable=import-error
from jmespath import compile as compile_jmes, Options

# get preview orchestrator version
preview = []
for key, value in result.items():
if key == "orchestrators":
for i in value:
if i["isPreview"]:
preview.append(i["orchestratorVersion"])

parsed = compile_jmes("""orchestrators[].{
kubernetesVersion: orchestratorVersion,
upgrades: upgrades[].orchestratorVersion || [`None available`] | sort_versions(@) | join(`, `, @)
kubernetesVersion: orchestratorVersion | set_preview(@),
upgrades: upgrades[].orchestratorVersion || [`None available`] | sort_versions(@) | set_preview_array(@) | join(`, `, @)
}""")
# use ordered dicts so headers are predictable
results = parsed.search(result, Options(dict_cls=OrderedDict, custom_functions=_custom_functions()))
results = parsed.search(result, Options(dict_cls=OrderedDict, custom_functions=_custom_functions(preview)))
return sorted(results, key=lambda x: version_to_tuple(x.get('kubernetesVersion')), reverse=True)


def version_to_tuple(v):
"""Quick-and-dirty sort function to handle simple semantic versions like 1.7.12 or 1.8.7."""
if v.endswith('(preview)'):
return tuple(map(int, (v[:-9].split('.'))))
return tuple(map(int, (v.split('.'))))


def _custom_functions():
def _custom_functions(preview_versions):
# pylint: disable=import-error
from jmespath import functions

Expand All @@ -103,4 +120,33 @@ def _func_sort_versions(self, s): # pylint: disable=no-self-use
except (TypeError, ValueError): # if it wasn't sortable, return the input so the pipeline continues
return s

@functions.signature({'types': ['array']})
def _func_set_preview_array(self, s): # pylint: disable=no-self-use
"""Custom JMESPath `set_preview_array` function that suffixes preview version"""
try:
res = []
for version in s:
preview = False
for i in preview_versions:
if version == i:
res.append(version + "(preview)")
preview = True
break
if not preview:
res.append(version)
return res
except(TypeError, ValueError):
return s

@functions.signature({'types': ['string']})
def _func_set_preview(self, s): # pylint: disable=no-self-use
"""Custom JMESPath `set_preview` function that suffixes preview version"""
try:
for i in preview_versions:
if s == i:
return s + "(preview)"
return s
except(TypeError, ValueError):
return s

return CustomFunctions()
9 changes: 9 additions & 0 deletions src/aks-preview/azext_aks_preview/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,3 +327,12 @@
text: az aks enable-addons --addons virtual-node --name MyManagedCluster --resource-group MyResourceGroup --subnet-name VirtualNodeSubnet
crafted: true
"""

helps['aks get-versions'] = """
type: command
short-summary: Get the versions available for creating a managed Kubernetes cluster.
examples:
- name: Get the versions available for creating a managed Kubernetes cluster
text: az aks get-versions --location westus2
crafted: true
"""
16 changes: 15 additions & 1 deletion src/aks-preview/azext_aks_preview/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
from azure.cli.core.commands import CliCommandType

from ._client_factory import cf_managed_clusters
from ._client_factory import cf_container_services
from ._client_factory import cf_agent_pools
from ._format import aks_show_table_format
from ._format import aks_agentpool_show_table_format
from ._format import aks_versions_table_format
from ._format import aks_upgrades_table_format


def load_command_table(self, _):
Expand All @@ -19,13 +22,19 @@ def load_command_table(self, _):
client_factory=cf_managed_clusters
)

container_services_sdk = CliCommandType(
operations_tmpl='azext_aks_preview.vendored_sdks.azure_mgmt_preview_aks.'
'operations.container_service_operations#ContainerServicesOperations.{}',
client_factory=cf_container_services
)

agent_pools_sdk = CliCommandType(
operations_tmpl='azext_aks_preview.vendored_sdks.azure_mgmt_preview_aks.'
'operations.agent_pools_operations#AgentPoolsOperations.{}',
client_factory=cf_managed_clusters
)

# AKS commands
# AKS managed cluster commands
with self.command_group('aks', managed_clusters_sdk, client_factory=cf_managed_clusters) as g:
g.custom_command('create', 'aks_create', supports_no_wait=True)
g.custom_command('update', 'aks_update', supports_no_wait=True)
Expand All @@ -36,8 +45,13 @@ def load_command_table(self, _):
g.custom_command('upgrade', 'aks_upgrade', supports_no_wait=True,
confirmation='Kubernetes may be unavailable during cluster upgrades.\n' +
'Are you sure you want to perform this operation?')
g.command('get-upgrades', 'get_upgrade_profile', table_transformer=aks_upgrades_table_format)
g.wait_command('wait')

# AKS container service commands
with self.command_group('aks', container_services_sdk, client_factory=cf_container_services) as g:
g.custom_command('get-versions', 'aks_get_versions', table_transformer=aks_versions_table_format)

# AKS agent pool commands
with self.command_group('aks nodepool', agent_pools_sdk, client_factory=cf_agent_pools) as g:
g.custom_command('list', 'aks_agentpool_list')
Expand Down
4 changes: 4 additions & 0 deletions src/aks-preview/azext_aks_preview/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -1198,3 +1198,7 @@ def _update_addons(cmd, instance, subscription_id, resource_group_name, addons,
instance.aad_profile = None

return instance


def aks_get_versions(cmd, client, location):
return client.list_orchestrators(location, resource_type='managedClusters')
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,13 @@ def test_aks_create_default_service_enable_autoscaler(self, resource_group, reso

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


def test_aks_get_versions(self):
# show k8s versions
self.cmd('aks get-versions -l {location}', checks=[
self.exists('orchestrators[*].orchestratorVersion')
])

@classmethod
def generate_ssh_keys(cls):
TEST_SSH_KEY_PUB = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCbIg1guRHbI0lV11wWDt1r2cUdcNd27CJsg+SfgC7miZeubtwUhbsPdhMQsfDyhOWHq1+ZL0M+nJZV63d/1dhmhtgyOqejUwrPlzKhydsbrsdUor+JmNJDdW01v7BXHyuymT8G4s09jCasNOwiufbP/qp72ruu0bIA1nySsvlf9pCQAuFkAnVnf/rFhUlOkhtRpwcq8SUNY2zRHR/EKb/4NWY1JzR4sa3q2fWIJdrrX0DvLoa5g9bIEd4Df79ba7v+yiUBOS0zT2ll+z4g9izHK3EO5d8hL4jYxcjKs+wcslSYRWrascfscLgMlMGh0CdKeNTDjHpGPncaf3Z+FwwwjWeuiNBxv7bJo13/8B/098KlVDl4GZqsoBCEjPyJfV6hO0y/LkRGkk7oHWKgeWAfKtfLItRp00eZ4fcJNK9kCaSMmEugoZWcI7NGbZXzqFWqbpRI7NcDP9+WIQ+i9U5vqWsqd/zng4kbuAJ6UuKqIzB0upYrLShfQE3SAck8oaLhJqqq56VfDuASNpJKidV+zq27HfSBmbXnkR/5AK337dc3MXKJypoK/QPMLKUAP5XLPbs+NddJQV7EZXd29DLgp+fRIg3edpKdO7ZErWhv7d+3Kws+e1Y+ypmR2WIVSwVyBEUfgv2C8Ts9gnTF4pNcEY/S2aBicz5Ew2+jdyGNQQ== [email protected]\n" # pylint: disable=line-too-long
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ def container_services(self):
api_version = self._get_api_version('container_services')
if api_version == '2017-07-01':
from .v2017_07_01.operations import ContainerServicesOperations as OperationClass
elif api_version == "2019-04-01":
from .v2019_04_01.operations import ContainerServicesOperations as OperationClass
else:
raise NotImplementedError("APIVersion {} is not available".format(api_version))
return OperationClass(self._client, self.config, Serializer(self._models_dict(api_version)), Deserializer(self._models_dict(api_version)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from .operations.operations import Operations
from .operations.managed_clusters_operations import ManagedClustersOperations
from .operations.agent_pools_operations import AgentPoolsOperations
from .operations.container_services_operations import ContainerServicesOperations
from . import models


Expand Down Expand Up @@ -65,6 +66,8 @@ class ContainerServiceClient(SDKClient):
:vartype managed_clusters: azure.mgmt.containerservice.v2019_04_01.operations.ManagedClustersOperations
:ivar agent_pools: AgentPools operations
:vartype agent_pools: azure.mgmt.containerservice.v2019_04_01.operations.AgentPoolsOperations
:ivar container_services: ContainerServices operations
:vartype container_services: azure.mgmt.containerservice.v2019_04_01.operations.ContainerServicesOperations
:param credentials: Credentials needed for the client to connect to Azure.
:type credentials: :mod:`A msrestazure Credentials
Expand Down Expand Up @@ -93,3 +96,5 @@ def __init__(
self._client, self.config, self._serialize, self._deserialize)
self.agent_pools = AgentPoolsOperations(
self._client, self.config, self._serialize, self._deserialize)
self.container_services = ContainerServicesOperations(
self._client, self.config, self._serialize, self._deserialize)
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,17 @@
from .container_service_diagnostics_profile_py3 import ContainerServiceDiagnosticsProfile
from .managed_cluster_addon_profile_py3 import ManagedClusterAddonProfile
from .managed_cluster_aad_profile_py3 import ManagedClusterAADProfile
from .managed_cluster_identity_py3 import ManagedClusterIdentity
from .managed_cluster_py3 import ManagedCluster
from .orchestrator_profile_py3 import OrchestratorProfile
from .managed_cluster_access_profile_py3 import ManagedClusterAccessProfile
from .managed_cluster_pool_upgrade_profile_upgrades_item_py3 import ManagedClusterPoolUpgradeProfileUpgradesItem
from .managed_cluster_pool_upgrade_profile_py3 import ManagedClusterPoolUpgradeProfile
from .managed_cluster_upgrade_profile_py3 import ManagedClusterUpgradeProfile
from .credential_result_py3 import CredentialResult
from .credential_results_py3 import CredentialResults
from .orchestrator_profile_py3 import OrchestratorProfile
from .orchestrator_version_profile_py3 import OrchestratorVersionProfile
from .orchestrator_version_profile_list_result_py3 import OrchestratorVersionProfileListResult
except (SyntaxError, ImportError):
from .operation_value import OperationValue
from .resource import Resource
Expand All @@ -54,13 +58,17 @@
from .container_service_diagnostics_profile import ContainerServiceDiagnosticsProfile
from .managed_cluster_addon_profile import ManagedClusterAddonProfile
from .managed_cluster_aad_profile import ManagedClusterAADProfile
from .managed_cluster_identity import ManagedClusterIdentity
from .managed_cluster import ManagedCluster
from .orchestrator_profile import OrchestratorProfile
from .managed_cluster_access_profile import ManagedClusterAccessProfile
from .managed_cluster_pool_upgrade_profile_upgrades_item import ManagedClusterPoolUpgradeProfileUpgradesItem
from .managed_cluster_pool_upgrade_profile import ManagedClusterPoolUpgradeProfile
from .managed_cluster_upgrade_profile import ManagedClusterUpgradeProfile
from .credential_result import CredentialResult
from .credential_results import CredentialResults
from .orchestrator_profile import OrchestratorProfile
from .orchestrator_version_profile import OrchestratorVersionProfile
from .orchestrator_version_profile_list_result import OrchestratorVersionProfileListResult
from .operation_value_paged import OperationValuePaged
from .managed_cluster_paged import ManagedClusterPaged
from .agent_pool_paged import AgentPoolPaged
Expand All @@ -71,6 +79,8 @@
AgentPoolType,
NetworkPlugin,
NetworkPolicy,
LoadBalancerSku,
ResourceIdentityType,
)

__all__ = [
Expand All @@ -92,13 +102,17 @@
'ContainerServiceDiagnosticsProfile',
'ManagedClusterAddonProfile',
'ManagedClusterAADProfile',
'ManagedClusterIdentity',
'ManagedCluster',
'OrchestratorProfile',
'ManagedClusterAccessProfile',
'ManagedClusterPoolUpgradeProfileUpgradesItem',
'ManagedClusterPoolUpgradeProfile',
'ManagedClusterUpgradeProfile',
'CredentialResult',
'CredentialResults',
'OrchestratorProfile',
'OrchestratorVersionProfile',
'OrchestratorVersionProfileListResult',
'OperationValuePaged',
'ManagedClusterPaged',
'AgentPoolPaged',
Expand All @@ -108,4 +122,6 @@
'AgentPoolType',
'NetworkPlugin',
'NetworkPolicy',
'LoadBalancerSku',
'ResourceIdentityType',
]
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,15 @@ class NetworkPolicy(str, Enum):

calico = "calico"
azure = "azure"


class LoadBalancerSku(str, Enum):

standard = "standard"
basic = "basic"


class ResourceIdentityType(str, Enum):

system_assigned = "SystemAssigned"
none = "None"
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ class ContainerServiceNetworkProfile(Model):
bridge network. It must not overlap with any Subnet IP ranges or the
Kubernetes service address range. Default value: "172.17.0.1/16" .
:type docker_bridge_cidr: str
:param load_balancer_sku: The load balancer sku for the managed cluster.
Possible values include: 'standard', 'basic'
:type load_balancer_sku: str or
~azure.mgmt.containerservice.v2019_04_01.models.LoadBalancerSku
"""

_validation = {
Expand All @@ -55,6 +59,7 @@ class ContainerServiceNetworkProfile(Model):
'service_cidr': {'key': 'serviceCidr', 'type': 'str'},
'dns_service_ip': {'key': 'dnsServiceIP', 'type': 'str'},
'docker_bridge_cidr': {'key': 'dockerBridgeCidr', 'type': 'str'},
'load_balancer_sku': {'key': 'loadBalancerSku', 'type': 'str'},
}

def __init__(self, **kwargs):
Expand All @@ -65,3 +70,4 @@ def __init__(self, **kwargs):
self.service_cidr = kwargs.get('service_cidr', "10.0.0.0/16")
self.dns_service_ip = kwargs.get('dns_service_ip', "10.0.0.10")
self.docker_bridge_cidr = kwargs.get('docker_bridge_cidr', "172.17.0.1/16")
self.load_balancer_sku = kwargs.get('load_balancer_sku', None)
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ class ContainerServiceNetworkProfile(Model):
bridge network. It must not overlap with any Subnet IP ranges or the
Kubernetes service address range. Default value: "172.17.0.1/16" .
:type docker_bridge_cidr: str
:param load_balancer_sku: The load balancer sku for the managed cluster.
Possible values include: 'standard', 'basic'
:type load_balancer_sku: str or
~azure.mgmt.containerservice.v2019_04_01.models.LoadBalancerSku
"""

_validation = {
Expand All @@ -55,13 +59,15 @@ class ContainerServiceNetworkProfile(Model):
'service_cidr': {'key': 'serviceCidr', 'type': 'str'},
'dns_service_ip': {'key': 'dnsServiceIP', 'type': 'str'},
'docker_bridge_cidr': {'key': 'dockerBridgeCidr', 'type': 'str'},
'load_balancer_sku': {'key': 'loadBalancerSku', 'type': 'str'},
}

def __init__(self, *, network_plugin="kubenet", network_policy=None, pod_cidr: str="10.244.0.0/16", service_cidr: str="10.0.0.0/16", dns_service_ip: str="10.0.0.10", docker_bridge_cidr: str="172.17.0.1/16", **kwargs) -> None:
def __init__(self, *, network_plugin="kubenet", network_policy=None, pod_cidr: str="10.244.0.0/16", service_cidr: str="10.0.0.0/16", dns_service_ip: str="10.0.0.10", docker_bridge_cidr: str="172.17.0.1/16", load_balancer_sku=None, **kwargs) -> None:
super(ContainerServiceNetworkProfile, self).__init__(**kwargs)
self.network_plugin = network_plugin
self.network_policy = network_policy
self.pod_cidr = pod_cidr
self.service_cidr = service_cidr
self.dns_service_ip = dns_service_ip
self.docker_bridge_cidr = docker_bridge_cidr
self.load_balancer_sku = load_balancer_sku
Loading

0 comments on commit aa0b237

Please sign in to comment.