Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Connectedk8s] Kubernetes distro and infra parameter and heuristics support #2620

Merged
merged 33 commits into from
Dec 9, 2020
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c2ee639
initial commit
Oct 30, 2020
d613f41
Updated helm install
Nov 2, 2020
2875ce9
Merge branch 'master' of https://github.com/Azure/azure-cli-extension…
Nov 2, 2020
494322d
Fixed
Nov 3, 2020
7728b56
cleaned up
Nov 3, 2020
725cb8c
Updated version
Nov 4, 2020
6598d1a
Merge branch 'master' of https://github.com/Azure/azure-cli-extension…
Nov 4, 2020
8600297
Fixed linting
Nov 4, 2020
f81d693
Added gke distro heuristics
alphaWizard Nov 5, 2020
145b59e
added eks,aws
alphaWizard Nov 6, 2020
51c17ee
Updated enum names and verified heuristics
Nov 11, 2020
01f9fa4
Fixed conflict
Nov 11, 2020
8db81f2
Merge branch 'master' of https://github.com/Azure/azure-cli-extension…
Nov 11, 2020
903b1c9
added new sdk
Nov 12, 2020
1cf82cc
Revert
Nov 13, 2020
64230e8
Added new sdk
Nov 25, 2020
ef6353b
Merge branch 'master' of https://github.com/Azure/azure-cli-extension…
Nov 25, 2020
49af79f
Fix
Nov 25, 2020
3a0753b
updated sdk
Nov 27, 2020
f5cd767
Delete code-model-v1
alphaWizard Nov 27, 2020
5895783
Update _params.py
alphaWizard Nov 30, 2020
95e2d07
Removed debug logs
Dec 1, 2020
aaebb2e
Merge branch 'distro-infra-feature' of https://github.com/alphaWizard…
Dec 1, 2020
9d9ade0
Fixed linting
Dec 1, 2020
6b9a43e
Added rancher_rke distro
Dec 1, 2020
cb86a2c
Incorporating comments
Dec 2, 2020
2af3c0f
Fix conflict
Dec 2, 2020
c4825bc
Fix conflict
Dec 2, 2020
31fe354
Updated distro/infra telemetry via get in update_agents
Dec 2, 2020
11141b7
Fix version
Dec 2, 2020
a5519b5
Merge
Dec 4, 2020
cac3a8d
Merge branch 'master' into distro-infra-feature
alphaWizard Dec 7, 2020
a65b10f
reverted back to 0.2.7
alphaWizard Dec 7, 2020
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
1 change: 1 addition & 0 deletions src/connectedk8s/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Release History
++++++
* Added checks for proxy and added disable-proxy
* Updated config dataplane endpoint to support other clouds
* `az connectedk8s connect`: Added support for kubernetes distro/infra parameters and heuristics

0.2.7
++++++
Expand Down
4 changes: 2 additions & 2 deletions src/connectedk8s/azext_connectedk8s/_client_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@


def cf_connectedk8s(cli_ctx, *_):
from azext_connectedk8s.vendored_sdks import KubernetesConnectRPClient
return get_mgmt_service_client(cli_ctx, KubernetesConnectRPClient)
from azext_connectedk8s.vendored_sdks import ConnectedKubernetesClient
return get_mgmt_service_client(cli_ctx, ConnectedKubernetesClient)


def cf_connected_cluster(cli_ctx, _):
Expand Down
4 changes: 4 additions & 0 deletions src/connectedk8s/azext_connectedk8s/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

# pylint: disable=line-too-long

Distribution_Enum_Values = ["auto", "generic", "openshift", "rancher_rke", "kind", "k3s", "minikube", "gke", "eks", "aks", "aks_hci", "capz", "aks_engine", "tkg"]
Infrastructure_Enum_Values = ["auto", "generic", "azure", "aws", "gcp", "azure_stack_hci", "azure_stack_hub", "azure_stack_edge", "vsphere"]

Azure_PublicCloudName = 'AZUREPUBLICCLOUD'
Azure_USGovCloudName = 'AZUREUSGOVERNMENTCLOUD'
Azure_DogfoodCloudName = 'AZUREDOGFOOD'
Expand Down Expand Up @@ -50,5 +53,6 @@
Kubeconfig_Failed_To_Load_Fault_Type = "failed-to-load-kubeconfig-file"
Proxy_Cert_Path_Does_Not_Exist_Fault_Type = 'proxy-cert-path-does-not-exist-error'
Proxy_Cert_Path_Does_Not_Exist_Error = 'Proxy cert path {} does not exist. Please check the path provided'
Get_Kubernetes_Infra_Fault_Type = 'kubernetes-get-infrastructure-error'
No_Param_Error = 'No parmeters were specified with update command. Please run az connectedk8s update --help to check parameters available for update'
EnableProxy_Conflict_Error = 'Conflict detected: --disable-proxy can not be set with --https-proxy, --http-proxy, --proxy-skip-range and --proxy-cert at the same time. Please run az connectedk8s update --help for more information about the parameters'
5 changes: 4 additions & 1 deletion src/connectedk8s/azext_connectedk8s/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
# pylint: disable=line-too-long

from argcomplete.completers import FilesCompleter
from azure.cli.core.commands.parameters import get_location_type
from azure.cli.core.commands.parameters import get_location_type, get_enum_type
from azure.cli.core.commands.parameters import (file_type)
from azure.cli.core.commands.validators import get_default_location_from_resource_group
from azext_connectedk8s._constants import Distribution_Enum_Values, Infrastructure_Enum_Values

Copy link
Contributor Author

@alphaWizard alphaWizard Nov 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"auto" is default cli value, which triggers heuristics evaluation, Remaining are the enum values.


def load_arguments(self, _):
Expand All @@ -24,6 +25,8 @@ def load_arguments(self, _):
c.argument('http_proxy', options_list=['--proxy-http'], help='Http proxy URL to be used.')
c.argument('no_proxy', options_list=['--proxy-skip-range'], help='List of URLs/CIDRs for which proxy should not to be used.')
c.argument('proxy_cert', options_list=['--proxy-cert'], type=file_type, completer=FilesCompleter(), help='Path to the certificate file for proxy')
c.argument('distribution', options_list=['--distribution'], help='The Kubernetes distribution which will be running on this connected cluster.', arg_type=get_enum_type(Distribution_Enum_Values))
c.argument('infrastructure', options_list=['--infrastructure'], help='The infrastructure on which the Kubernetes cluster represented by this connected cluster will be running on.', arg_type=get_enum_type(Infrastructure_Enum_Values))

with self.argument_context('connectedk8s update') as c:
c.argument('cluster_name', options_list=['--name', '-n'], id_part='name', help='The name of the connected cluster.')
Expand Down
7 changes: 7 additions & 0 deletions src/connectedk8s/azext_connectedk8s/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,10 @@ def kubernetes_exception_handler(ex, fault_type, summary, error_message='Error o
if raise_error:
telemetry.set_exception(exception=ex, fault_type=fault_type, summary=summary)
raise CLIError(error_message + "\nError: " + str(ex))


def validate_infrastructure_type(infra):
for s in consts.Infrastructure_Enum_Values[1:]: # First value is "auto"
if s.lower() == infra.lower():
return s
return "generic"
106 changes: 86 additions & 20 deletions src/connectedk8s/azext_connectedk8s/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@


def create_connectedk8s(cmd, client, resource_group_name, cluster_name, https_proxy="", http_proxy="", no_proxy="", proxy_cert="", location=None,
kube_config=None, kube_context=None, no_wait=False, tags=None):
kube_config=None, kube_context=None, no_wait=False, tags=None, distribution='auto', infrastructure='auto'):
logger.warning("Ensure that you have the latest helm version installed before proceeding.")
logger.warning("This operation might take a while...\n")

Expand Down Expand Up @@ -108,13 +108,21 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, https_pr
# if the user had not logged in.
check_kube_connection(configuration)

# Get kubernetes cluster info for telemetry
# Get kubernetes cluster info
kubernetes_version = get_server_version(configuration)
kubernetes_distro = get_kubernetes_distro(configuration)
if distribution == 'auto':
kubernetes_distro = get_kubernetes_distro(configuration) # (cluster heuristics)
else:
kubernetes_distro = distribution
if infrastructure == 'auto':
kubernetes_infra = get_kubernetes_infra(configuration) # (cluster heuristics)
else:
kubernetes_infra = infrastructure

kubernetes_properties = {
'Context.Default.AzureCLI.KubernetesVersion': kubernetes_version,
'Context.Default.AzureCLI.KubernetesDistro': kubernetes_distro
'Context.Default.AzureCLI.KubernetesDistro': kubernetes_distro,
'Context.Default.AzureCLI.KubernetesInfra': kubernetes_infra
}
telemetry.add_extension_event('connectedk8s', kubernetes_properties)

Expand Down Expand Up @@ -161,7 +169,7 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, https_pr
configmap_cluster_name).agent_public_key_certificate
except Exception as e: # pylint: disable=broad-except
utils.arm_exception_handler(e, consts.Get_ConnectedCluster_Fault_Type, 'Failed to check if connected cluster resource already exists.')
cc = generate_request_payload(configuration, location, public_key, tags)
cc = generate_request_payload(configuration, location, public_key, tags, kubernetes_distro, kubernetes_infra)
create_cc_resource(client, resource_group_name, cluster_name, cc, no_wait)
else:
telemetry.set_user_fault()
Expand Down Expand Up @@ -229,13 +237,13 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, https_pr
raise CLIError("Failed to export private key." + str(e))

# Generate request payload
cc = generate_request_payload(configuration, location, public_key, tags)
cc = generate_request_payload(configuration, location, public_key, tags, kubernetes_distro, kubernetes_infra)

# Create connected cluster resource
put_cc_response = create_cc_resource(client, resource_group_name, cluster_name, cc, no_wait)

# Install azure-arc agents
helm_install_release(chart_path, subscription_id, kubernetes_distro, resource_group_name, cluster_name,
helm_install_release(chart_path, subscription_id, kubernetes_distro, kubernetes_infra, resource_group_name, cluster_name,
location, onboarding_tenant_id, http_proxy, https_proxy, no_proxy, proxy_cert, private_key_pem, kube_config,
kube_context, no_wait, values_file_provided, values_file, azure_cloud)

Expand Down Expand Up @@ -415,22 +423,67 @@ def get_server_version(configuration):
raise_error=False)


def get_kubernetes_distro(configuration):
def get_kubernetes_distro(configuration): # Heuristic
api_instance = kube_client.CoreV1Api(kube_client.ApiClient(configuration))
try:
api_response = api_instance.list_node()
if api_response.items:
labels = api_response.items[0].metadata.labels
if labels.get("node.openshift.io/os_id") == "rhcos" or labels.get("node.openshift.io/os_id") == "rhel":
provider_id = str(api_response.items[0].spec.provider_id)
annotations = list(api_response.items)[0].metadata.annotations
if labels.get("node.openshift.io/os_id"):
return "openshift"
return "default"
if labels.get("kubernetes.azure.com/node-image-version"):
return "aks"
if labels.get("cloud.google.com/gke-nodepool") or labels.get("cloud.google.com/gke-os-distribution"):
return "gke"
if labels.get("eks.amazonaws.com/nodegroup"):
return "eks"
if labels.get("minikube.k8s.io/version"):
return "minikube"
if provider_id.startswith("kind://"):
return "kind"
if provider_id.startswith("k3s://"):
return "k3s"
if annotations.get("rke.cattle.io/external-ip") or annotations.get("rke.cattle.io/internal-ip"):
return "rancher_rke"
if provider_id.startswith("moc://"): # Todo: ask from aks hci team for more reliable identifier in node labels,etc
return "generic" # return "aks_hci"
return "generic"
except Exception as e: # pylint: disable=broad-except
logger.warning("Error occured while trying to fetch kubernetes distribution.")
utils.kubernetes_exception_handler(e, consts.Get_Kubernetes_Distro_Fault_Type, 'Unable to fetch kubernetes distribution',
raise_error=False)
return "generic"


def generate_request_payload(configuration, location, public_key, tags):
def get_kubernetes_infra(configuration): # Heuristic
api_instance = kube_client.CoreV1Api(kube_client.ApiClient(configuration))
try:
api_response = api_instance.list_node()
if api_response.items:
provider_id = str(api_response.items[0].spec.provider_id)
infra = provider_id.split(':')[0]
if infra == "k3s" or infra == "kind":
return "generic"
if infra == "azure":
return "azure"
if infra == "gce":
return "gcp"
if infra == "aws":
return "aws"
if infra == "moc": # Todo: ask from aks hci team for more reliable identifier in node labels,etc
return "generic" # return "azure_stack_hci"
return utils.validate_infrastructure_type(infra)
return "generic"
except Exception as e: # pylint: disable=broad-except
logger.warning("Error occured while trying to fetch kubernetes infrastructure.")
utils.kubernetes_exception_handler(e, consts.Get_Kubernetes_Infra_Fault_Type, 'Unable to fetch kubernetes infrastructure',
raise_error=False)
return "generic"


def generate_request_payload(configuration, location, public_key, tags, kubernetes_distro, kubernetes_infra):
# Create connected cluster resource object
aad_profile = ConnectedClusterAADProfile(
tenant_id="",
Expand All @@ -447,7 +500,9 @@ def generate_request_payload(configuration, location, public_key, tags):
identity=identity,
agent_public_key_certificate=public_key,
aad_profile=aad_profile,
tags=tags
tags=tags,
distribution=kubernetes_distro,
infrastructure=kubernetes_infra
)
return cc

Expand Down Expand Up @@ -598,12 +653,13 @@ def get_release_namespace(kube_config, kube_context):
return None


def helm_install_release(chart_path, subscription_id, kubernetes_distro, resource_group_name, cluster_name,
def helm_install_release(chart_path, subscription_id, kubernetes_distro, kubernetes_infra, resource_group_name, cluster_name,
location, onboarding_tenant_id, http_proxy, https_proxy, no_proxy, proxy_cert, private_key_pem,
kube_config, kube_context, no_wait, values_file_provided, values_file, cloud_name):
cmd_helm_install = ["helm", "upgrade", "--install", "azure-arc", chart_path,
"--set", "global.subscriptionId={}".format(subscription_id),
"--set", "global.kubernetesDistro={}".format(kubernetes_distro),
"--set", "global.kubernetesInfra={}".format(kubernetes_infra),
"--set", "global.resourceGroupName={}".format(resource_group_name),
"--set", "global.resourceName={}".format(cluster_name),
"--set", "global.location={}".format(location),
Expand Down Expand Up @@ -779,13 +835,6 @@ def update_agents(cmd, client, resource_group_name, cluster_name, https_proxy=""

# Get kubernetes cluster info for telemetry
kubernetes_version = get_server_version(configuration)
kubernetes_distro = get_kubernetes_distro(configuration)

kubernetes_properties = {
'Context.Default.AzureCLI.KubernetesVersion': kubernetes_version,
'Context.Default.AzureCLI.KubernetesDistro': kubernetes_distro
alphaWizard marked this conversation as resolved.
Show resolved Hide resolved
}
telemetry.add_extension_event('connectedk8s', kubernetes_properties)

# Checking helm installation
check_helm_install(kube_config, kube_context)
Expand All @@ -811,6 +860,23 @@ def update_agents(cmd, client, resource_group_name, cluster_name, https_proxy=""
# Fetch Connected Cluster for agent version
connected_cluster = get_connectedk8s(cmd, client, resource_group_name, cluster_name)

if hasattr(connected_cluster, 'distribution') and (connected_cluster.distribution is not None):
kubernetes_distro = connected_cluster.distribution
else:
kubernetes_distro = get_kubernetes_distro(configuration)

if hasattr(connected_cluster, 'infrastructure') and (connected_cluster.infrastructure is not None):
kubernetes_infra = connected_cluster.infrastructure
else:
kubernetes_infra = get_kubernetes_infra(configuration)

kubernetes_properties = {
'Context.Default.AzureCLI.KubernetesVersion': kubernetes_version,
'Context.Default.AzureCLI.KubernetesDistro': kubernetes_distro,
'Context.Default.AzureCLI.KubernetesInfra': kubernetes_infra
}
telemetry.add_extension_event('connectedk8s', kubernetes_properties)

# Adding helm repo
if os.getenv('HELMREPONAME') and os.getenv('HELMREPOURL'):
utils.add_helm_repo(kube_config, kube_context)
Expand Down
4 changes: 2 additions & 2 deletions src/connectedk8s/azext_connectedk8s/vendored_sdks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
# regenerated.
# --------------------------------------------------------------------------

from .kubernetes_connect_rp_client import KubernetesConnectRPClient
from .connected_kubernetes_client import ConnectedKubernetesClient
from .version import VERSION

__all__ = ['KubernetesConnectRPClient']
__all__ = ['ConnectedKubernetesClient']

__version__ = VERSION

Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
from . import models


class KubernetesConnectRPClientConfiguration(AzureConfiguration):
"""Configuration for KubernetesConnectRPClient
class ConnectedKubernetesClientConfiguration(AzureConfiguration):
"""Configuration for ConnectedKubernetesClient
Note that all parameters used to create this instance are saved as instance
attributes.

Expand All @@ -41,7 +41,7 @@ def __init__(
if not base_url:
base_url = 'https://management.azure.com'

super(KubernetesConnectRPClientConfiguration, self).__init__(base_url)
super(ConnectedKubernetesClientConfiguration, self).__init__(base_url)

self.add_user_agent('azure-mgmt-hybridkubernetes/{}'.format(VERSION))
self.add_user_agent('Azure-SDK-For-Python')
Expand All @@ -50,16 +50,16 @@ def __init__(
self.subscription_id = subscription_id


class KubernetesConnectRPClient(SDKClient):
class ConnectedKubernetesClient(SDKClient):
"""Azure Connected Cluster Resource Provider API for adopting any Kubernetes Cluster

:ivar config: Configuration for client.
:vartype config: KubernetesConnectRPClientConfiguration
:vartype config: ConnectedKubernetesClientConfiguration

:ivar connected_cluster: ConnectedCluster operations
:vartype connected_cluster: azure.mgmt.hybridkubernetes.operations.ConnectedClusterOperations
:vartype connected_cluster: azure.mgmt.hybridkubernetes.v2020_01_01_preview.operations.ConnectedClusterOperations
:ivar operations: Operations operations
:vartype operations: azure.mgmt.hybridkubernetes.operations.Operations
:vartype operations: azure.mgmt.hybridkubernetes.v2020_01_01_preview.operations.Operations

:param credentials: Credentials needed for the client to connect to Azure.
:type credentials: :mod:`A msrestazure Credentials
Expand All @@ -72,8 +72,8 @@ class KubernetesConnectRPClient(SDKClient):
def __init__(
self, credentials, subscription_id, base_url=None):

self.config = KubernetesConnectRPClientConfiguration(credentials, subscription_id, base_url)
super(KubernetesConnectRPClient, self).__init__(self.config.credentials, self.config)
self.config = ConnectedKubernetesClientConfiguration(credentials, subscription_id, base_url)
super(ConnectedKubernetesClient, self).__init__(self.config.credentials, self.config)

client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)}
self.api_version = '2020-01-01-preview'
Expand Down
Loading