Skip to content

Commit 9483a98

Browse files
changes to support gateway association/disassociation for api version '2025-08-01-preview' (#17)
1 parent 4d39530 commit 9483a98

File tree

40 files changed

+8095
-6326
lines changed

40 files changed

+8095
-6326
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888

8989
/src/ip-group/ @necusjz @kairu-ms @jsntcy
9090

91-
/src/connectedk8s/ @bavneetsingh16 @deeksha345 @anagg929 @atchutbarli
91+
/src/connectedk8s/ @bavneetsingh16 @deeksha345 @anagg929 @atchutbarli @bgriddaluru
9292

9393
/src/storagesync/ @jsntcy
9494

src/connectedk8s/azext_connectedk8s/_client_factory.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@
2727

2828
from azext_connectedk8s.vendored_sdks import ConnectedKubernetesClient
2929
from azext_connectedk8s.vendored_sdks.operations import ConnectedClusterOperations
30-
from azext_connectedk8s.vendored_sdks.preview_2024_07_01 import (
31-
ConnectedKubernetesClient as ConnectedKubernetesClient20240701,
30+
from azext_connectedk8s.vendored_sdks.preview_2025_08_01 import (
31+
KubernetesClient as ConnectedKubernetesClient20250801,
3232
)
33-
from azext_connectedk8s.vendored_sdks.preview_2024_07_01.operations import (
34-
ConnectedClusterOperations as ConnectedClusterOperations20240701,
33+
from azext_connectedk8s.vendored_sdks.preview_2025_08_01.operations import (
34+
ConnectedClusterOperations as ConnectedClusterOperations20250801,
3535
)
3636

3737
AccessToken = namedtuple("AccessToken", ["token", "expires_on"])
@@ -61,34 +61,46 @@ def cf_connected_cluster(cli_ctx: AzCli, _: Any) -> ConnectedClusterOperations:
6161
return cf_connectedk8s(cli_ctx).connected_cluster
6262

6363

64-
def cf_connectedk8s_prev_2024_07_01(
64+
def cf_connectedk8s_prev_2025_08_01(
6565
cli_ctx: AzCli, *_: Any
66-
) -> ConnectedKubernetesClient20240701:
67-
from azext_connectedk8s.vendored_sdks.preview_2024_07_01 import (
68-
ConnectedKubernetesClient,
66+
) -> ConnectedKubernetesClient20250801:
67+
from azure.core.pipeline.policies import HeadersPolicy
68+
69+
from azext_connectedk8s.vendored_sdks.preview_2025_08_01 import (
70+
KubernetesClient,
6971
)
7072

71-
client: ConnectedKubernetesClient
73+
# Create custom headers policy for PUT requests
74+
headers_policy = HeadersPolicy({"x-ms-azurearc-cli": "true"})
75+
76+
client: KubernetesClient
7277
access_token = os.getenv(consts.Azure_Access_Token_Variable)
7378
if access_token is not None:
7479
validate_custom_token()
7580
credential = AccessTokenCredential(access_token=access_token)
7681
client = get_mgmt_service_client(
7782
cli_ctx,
78-
ConnectedKubernetesClient,
83+
KubernetesClient,
7984
subscription_id=os.getenv("AZURE_SUBSCRIPTION_ID"),
8085
credential=credential,
86+
base_url="https://management.azure.com",
87+
per_call_policies=[headers_policy],
8188
)
8289
return client
8390

84-
client = get_mgmt_service_client(cli_ctx, ConnectedKubernetesClient)
91+
client = get_mgmt_service_client(
92+
cli_ctx,
93+
KubernetesClient,
94+
base_url="https://management.azure.com",
95+
per_call_policies=[headers_policy],
96+
)
8597
return client
8698

8799

88-
def cf_connected_cluster_prev_2024_07_01(
100+
def cf_connected_cluster_prev_2025_08_01(
89101
cli_ctx: AzCli, _: Any
90-
) -> ConnectedClusterOperations20240701:
91-
return cf_connectedk8s_prev_2024_07_01(cli_ctx).connected_cluster
102+
) -> ConnectedClusterOperations20250801:
103+
return cf_connectedk8s_prev_2025_08_01(cli_ctx).connected_cluster
92104

93105

94106
def cf_connectedmachine(

src/connectedk8s/azext_connectedk8s/_constants.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@
418418

419419
# Connect Precheck Diagnoser constants
420420
Cluster_Diagnostic_Checks_Job_Registry_Path = (
421-
"azurearck8s/helmchart/stable/clusterdiagnosticchecks:0.2.2"
421+
"azurearck8s/helmchart/stable/clusterdiagnosticchecks:1.29.3"
422422
)
423423
Cluster_Diagnostic_Checks_Helm_Install_Failed_Fault_Type = (
424424
"Error while installing cluster diagnostic checks helm release"
@@ -525,3 +525,9 @@
525525
# "Application code shouldn't block the creation of resources for a resource provider that is in the registering state."
526526
# See https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/resource-providers-and-types#register-resource-provider
527527
allowed_rp_registration_states = ["Registering", "Registered"]
528+
529+
GATEWAY_LINK_FAULT_TYPE = "gateway-link-error"
530+
Gateway_Cluster_Resource_Update_Failed_Fault_Type = (
531+
"Gateway-Cluster-Resource-Update-Failed"
532+
)
533+
GATEWAY_ASSOCIATE_URL = "https://management.azure.com/subscriptions/{subscription_id}/resourceGroups/{resource_group}/providers/Microsoft.Kubernetes/connectedClusters/{cluster_name}/providers/Microsoft.HybridCompute/settings/Default?api-version={api_version}"

src/connectedk8s/azext_connectedk8s/_troubleshootutils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
if TYPE_CHECKING:
2525
from kubernetes.client import AppsV1Api, BatchV1Api, CoreV1Api
2626

27-
from .vendored_sdks.preview_2024_07_01.models import (
27+
from .vendored_sdks.preview_2025_08_01.models import (
2828
ConnectedCluster,
2929
)
3030

@@ -1000,7 +1000,7 @@ def check_agent_version(
10001000
return consts.Diagnostic_Check_Incomplete
10011001

10021002
# To get user agent version and the latest agent version
1003-
user_agent_version = connected_cluster.agent_version # type: ignore[unreachable]
1003+
user_agent_version = connected_cluster.agent_version
10041004
current_user_version = user_agent_version.split(".")
10051005
latest_agent_version = azure_arc_agent_version.split(".")
10061006
# Comparing if the user version is compatible or not

src/connectedk8s/azext_connectedk8s/_utils.py

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
from kubernetes.client import CoreV1Api, V1NodeList
4949
from requests import Response
5050

51-
from azext_connectedk8s.vendored_sdks.preview_2024_07_01.models import (
51+
from azext_connectedk8s.vendored_sdks.preview_2025_08_01.models import (
5252
ConnectedCluster,
5353
)
5454

@@ -823,7 +823,7 @@ def get_helm_values(
823823
chart_location_url = f"{config_dp_endpoint}/{chart_location_url_segment}"
824824
dp_request_identity = connected_cluster.identity
825825
identity = connected_cluster.id
826-
request_dict = connected_cluster.serialize()
826+
request_dict = connected_cluster.as_dict()
827827
request_dict["identity"]["tenantId"] = dp_request_identity.tenant_id
828828
request_dict["identity"]["principalId"] = dp_request_identity.principal_id
829829
request_dict["id"] = identity
@@ -903,6 +903,70 @@ def health_check_dp(cmd: CLICommand, config_dp_endpoint: str) -> bool:
903903
raise CLIInternalError("Error while performing DP health check")
904904

905905

906+
def update_gateway_cluster_link(
907+
cmd: CLICommand,
908+
subscription_id: str,
909+
resource_group: str,
910+
cluster_name: str,
911+
gateway_resource_id: str | None = None,
912+
) -> bool:
913+
"""
914+
Associates or disassociates a gateway with a cluster.
915+
916+
If `gateway_resource_id` is provided, performs association.
917+
If `gateway_resource_id` is None, performs disassociation.
918+
"""
919+
api_version = "2025-02-19-preview"
920+
is_association = gateway_resource_id is not None
921+
resource = cmd.cli_ctx.cloud.endpoints.active_directory_resource_id
922+
url = consts.GATEWAY_ASSOCIATE_URL.format(
923+
subscription_id=subscription_id,
924+
resource_group=resource_group,
925+
cluster_name=cluster_name,
926+
api_version=api_version,
927+
)
928+
929+
headers = ["Content-Type=application/json", "Accept=application/json"]
930+
931+
token = os.getenv("AZURE_ACCESS_TOKEN")
932+
if token:
933+
headers.append(f"Authorization=Bearer {token}")
934+
935+
operation_type = "association" if is_association else "disassociation"
936+
body = {
937+
"properties": {
938+
"gatewayProperties": {
939+
"gatewayResourceId": gateway_resource_id # None in case of disassociation
940+
}
941+
}
942+
}
943+
response = send_request_with_retries(
944+
cmd.cli_ctx,
945+
method="put",
946+
url=url,
947+
headers=headers,
948+
fault_type=consts.GATEWAY_LINK_FAULT_TYPE,
949+
summary=f"Error during gateway {operation_type}",
950+
request_body=json.dumps(body),
951+
resource=resource,
952+
)
953+
954+
if response.status_code == 200:
955+
logger.info(
956+
f"Gateway {operation_type} succeeded for cluster '{cluster_name}' in resource group '{resource_group}'."
957+
)
958+
return True
959+
960+
telemetry.set_exception(
961+
exception=f"Gateway {operation_type} failed",
962+
fault_type=consts.GATEWAY_LINK_FAULT_TYPE,
963+
summary=f"Gateway {operation_type} failed",
964+
)
965+
raise CLIInternalError(
966+
f"Gateway {operation_type} failed for cluster '{cluster_name}'."
967+
)
968+
969+
906970
def send_request_with_retries(
907971
cli_ctx: AzCli,
908972
method: str,
@@ -992,10 +1056,10 @@ def arm_exception_handler(
9921056
status_code = ex.status_code
9931057
if status_code == 404 and return_if_not_found:
9941058
return
995-
if status_code // 100 == 4:
1059+
if status_code is not None and status_code // 100 == 4:
9961060
telemetry.set_user_fault()
9971061
telemetry.set_exception(exception=ex, fault_type=fault_type, summary=summary)
998-
if status_code // 100 == 5:
1062+
if status_code is not None and status_code // 100 == 5:
9991063
raise AzureInternalError(
10001064
"Http response error occured while making ARM request: "
10011065
+ str(ex)

src/connectedk8s/azext_connectedk8s/clientproxyhelper/_proxylogic.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import azext_connectedk8s._constants as consts
1212
import azext_connectedk8s.clientproxyhelper._utils as clientproxyutils
1313

14-
from ..vendored_sdks.models import (
14+
from ..vendored_sdks.preview_2025_08_01.models import (
1515
ListClusterUserCredentialProperties,
1616
)
1717

@@ -21,10 +21,10 @@
2121
from knack.commands import CLICommand
2222
from requests.models import Response
2323

24-
from azext_connectedk8s.vendored_sdks.preview_2024_07_01.models import (
24+
from azext_connectedk8s.vendored_sdks.preview_2025_08_01.models import (
2525
CredentialResults,
2626
)
27-
from azext_connectedk8s.vendored_sdks.preview_2024_07_01.operations import (
27+
from azext_connectedk8s.vendored_sdks.preview_2025_08_01.operations import (
2828
ConnectedClusterOperations,
2929
)
3030

@@ -83,7 +83,7 @@ def get_cluster_user_credentials(
8383
authentication_method=auth_method, client_proxy=True
8484
)
8585

86-
result: CredentialResults = client.list_cluster_user_credential( # type: ignore[call-overload]
86+
result: CredentialResults = client.list_cluster_user_credential(
8787
resource_group_name,
8888
cluster_name,
8989
list_prop,

src/connectedk8s/azext_connectedk8s/clientproxyhelper/_utils.py

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434

3535
from knack.commands import CLICommand
3636

37-
from azext_connectedk8s.vendored_sdks.preview_2024_07_01.models import (
37+
from azext_connectedk8s.vendored_sdks.preview_2025_08_01.models import (
3838
CredentialResults,
3939
)
4040

@@ -204,17 +204,31 @@ def prepare_clientproxy_data(response: CredentialResults) -> dict[str, Any]:
204204
data["kubeconfigs"] = []
205205
kubeconfig = {}
206206
kubeconfig["name"] = "Kubeconfig"
207-
kubeconfig["value"] = b64encode(response.kubeconfigs[0].value).decode("utf-8") # type: ignore[index]
207+
208+
# Check if kubeconfigs exists and has items
209+
if response.kubeconfigs and len(response.kubeconfigs) > 0:
210+
kubeconfig_value = response.kubeconfigs[0].value
211+
if kubeconfig_value is not None:
212+
kubeconfig["value"] = b64encode(kubeconfig_value).decode("utf-8")
213+
208214
data["kubeconfigs"].append(kubeconfig)
209215
data["hybridConnectionConfig"] = {}
210-
data["hybridConnectionConfig"]["relay"] = response.hybrid_connection_config.relay # type: ignore[attr-defined]
211-
data["hybridConnectionConfig"]["hybridConnectionName"] = (
212-
response.hybrid_connection_config.hybrid_connection_name # type: ignore[attr-defined]
213-
)
214-
data["hybridConnectionConfig"]["token"] = response.hybrid_connection_config.token # type: ignore[attr-defined]
215-
data["hybridConnectionConfig"]["expirationTime"] = (
216-
response.hybrid_connection_config.expiration_time # type: ignore[attr-defined]
217-
)
216+
217+
# Check if hybrid_connection_config exists
218+
if response.hybrid_connection_config is not None:
219+
data["hybridConnectionConfig"]["relay"] = (
220+
response.hybrid_connection_config.relay
221+
)
222+
data["hybridConnectionConfig"]["hybridConnectionName"] = (
223+
response.hybrid_connection_config.hybrid_connection_name
224+
)
225+
data["hybridConnectionConfig"]["token"] = (
226+
response.hybrid_connection_config.token
227+
)
228+
data["hybridConnectionConfig"]["expirationTime"] = (
229+
response.hybrid_connection_config.expiration_time
230+
)
231+
218232
return data
219233

220234

src/connectedk8s/azext_connectedk8s/commands.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
from azure.cli.core.commands import CliCommandType
1212

1313
from azext_connectedk8s._client_factory import (
14-
cf_connected_cluster_prev_2024_07_01,
15-
cf_connectedk8s_prev_2024_07_01,
14+
cf_connected_cluster_prev_2025_08_01,
15+
cf_connectedk8s_prev_2025_08_01,
1616
)
1717

1818
from ._format import connectedk8s_list_table_format, connectedk8s_show_table_format
@@ -27,12 +27,12 @@ def load_command_table(self: Connectedk8sCommandsLoader, _: list[str] | None) ->
2727
"azext_connectedk8s.vendored_sdks.preview_2024_07_01.operations#"
2828
"ConnectedClusterOperations.{}"
2929
),
30-
client_factory=cf_connectedk8s_prev_2024_07_01,
30+
client_factory=cf_connectedk8s_prev_2025_08_01,
3131
)
3232
with self.command_group(
3333
"connectedk8s",
3434
connectedk8s_sdk,
35-
client_factory=cf_connected_cluster_prev_2024_07_01,
35+
client_factory=cf_connected_cluster_prev_2025_08_01,
3636
) as g:
3737
g.custom_command("connect", "create_connectedk8s", supports_no_wait=True)
3838
g.custom_command("update", "update_connected_cluster")

0 commit comments

Comments
 (0)