Skip to content

Commit 34db00e

Browse files
committed
Add new vme command list
1 parent 1e27e0d commit 34db00e

File tree

8 files changed

+135
-18
lines changed

8 files changed

+135
-18
lines changed

src/vme/azext_vme/_format.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
# --------------------------------------------------------------------------------------------
5+
6+
from collections import OrderedDict
7+
8+
9+
def vme_list_table_format(results):
10+
return [__get_table_row(result) for result in results]
11+
12+
13+
def __get_table_row(result):
14+
return OrderedDict([
15+
('name', result['name']),
16+
('extensionType', result.get('properties', {}).get('extensionType', '')),
17+
('version', result.get('properties', {}).get('version', '')),
18+
('provisioningState', result.get('properties', {}).get('provisioningState', '')),
19+
('isSystemExtension', result.get('properties', {}).get('isSystemExtension', '')),
20+
('lastModifiedAt', result.get('systemData', {}).get('lastModifiedAt', '')),
21+
])

src/vme/azext_vme/_help.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,15 @@
4747
text: |-
4848
az vme upgrade --resource-group my-resource-group --cluster-name my-cluster --wait
4949
"""
50+
51+
helps['vme list'] = """
52+
type: command
53+
short-summary: List version managed extensions.
54+
examples:
55+
- name: List version managed extensions
56+
text: |-
57+
az vme list --resource-group my-resource-group --cluster-name my-cluster
58+
- name: List version managed extensions with table format
59+
text: |-
60+
az vme list --resource-group my-resource-group --cluster-name my-cluster --output table
61+
"""

src/vme/azext_vme/_params.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,15 @@ def load_arguments(self, _):
9292
help='Extension types to be uninstalled.',
9393
arg_type=get_enum_type(IncludedExtensionTypes),
9494
)
95+
96+
with self.argument_context('vme list') as c:
97+
c.argument(
98+
'resource_group',
99+
options_list=['--resource-group', '-g'],
100+
help='Name of the resource group'
101+
)
102+
c.argument(
103+
'cluster_name',
104+
options_list=['--cluster-name', '-c'],
105+
help='Name of the Kubernetes cluster'
106+
)

src/vme/azext_vme/commands.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
# --------------------------------------------------------------------------------------------
55

66
# pylint: disable=line-too-long
7+
from ._format import vme_list_table_format
8+
79

810
def load_command_table(self, _):
911

1012
with self.command_group('vme') as g:
1113
g.custom_command('upgrade', 'upgrade_vme')
1214
g.custom_command('install', 'install_vme')
1315
g.custom_command('uninstall', 'uninstall_vme')
16+
g.custom_command('list', 'list_vme', table_transformer=vme_list_table_format)

src/vme/azext_vme/consts.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@
2020
SUCCEEDED = "Succeeded"
2121
CANCELLED = "Cancelled"
2222
Bundle_FeatureFlag_NotEnabled = "Bundle feature flag is not enabled."
23-
UPGRADE_IN_PROGRESS_MSG = "Version managed extensions upgrade is in progress"
23+
UPGRADE_IN_PROGRESS_MSG = "Version managed extensions upgrade is in progress..."
2424
UPGRADE_SUCCEEDED_MSG = "Version managed extensions upgrade completed!"
2525
UPGRADE_FAILED_MSG = "Version managed extensions upgrade failed. Error: "
2626
UPGRADE_CANCELED_MSG = "Version managed extensions upgrade is canceled."
27-
UPGRADE_NOTSTARTED_MSG = "Waiting for version managed extensions upgrade to start"
27+
UPGRADE_NOTSTARTED_MSG = "Waiting for version managed extensions upgrade to start..."
2828
UPGRADE_TIMEOUT_MSG = """
2929
Error: version managed extensions upgrade could not start in {0} seconds. Check out common issues here: <url>
3030
"""
@@ -34,9 +34,14 @@
3434
"microsoft.azure.secretstore"
3535
]
3636

37-
BundleExtensionNames = {
37+
BundleExtensionTypeNames = {
3838
"microsoft.arc.containerstorage": "azure-arc-containerstorage",
3939
"microsoft.azure.secretstore": "azure-secret-store",
4040
}
4141

4242
IncludedExtensionTypes = BundleExtensionTypes + ["all"]
43+
BundleExtensionNames = [
44+
"azure-arc-containerstorage",
45+
"azure-secret-store",
46+
"microsoft.extensiondiagnostics-v0"
47+
]

src/vme/azext_vme/custom.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,12 @@ def install_vme(
4141
for extension_type in include_extension_types:
4242
extension_resource_id = (
4343
f"{cluster_resource_id}/Providers/Microsoft.KubernetesConfiguration/"
44-
f"extensions/{consts.BundleExtensionNames[extension_type]}"
44+
f"extensions/{consts.BundleExtensionTypeNames[extension_type]}"
4545
)
4646
try:
47-
resources.get_by_id(extension_resource_id, '2022-11-01')
47+
ext = resources.get_by_id(extension_resource_id, '2022-11-01')
48+
if ext.properties['provisioningState'] == 'Failed':
49+
raise ResourceNotFoundError()
4850
print(f"Extension {extension_type} already exists, skipping installation.")
4951
continue
5052
except ResourceNotFoundError:
@@ -61,14 +63,15 @@ def install_vme(
6163
"--cluster-type",
6264
consts.CONNECTEDCLUSTER_TYPE,
6365
"--name",
64-
consts.BundleExtensionNames[extension_type],
66+
consts.BundleExtensionTypeNames[extension_type],
6567
"--extension-type",
6668
extension_type,
6769
"--scope",
6870
"cluster"
6971
]
70-
utils.call_subprocess_raise_output(command)
72+
result = utils.call_subprocess_raise_output(command)
7173
print(f"Installed extension {extension_type} successfully.")
74+
print(result)
7275

7376
print("All extensions installed successfully.")
7477

@@ -103,7 +106,7 @@ def uninstall_vme(
103106
"--cluster-type",
104107
"connectedClusters",
105108
"--name",
106-
consts.BundleExtensionNames[extension_type],
109+
consts.BundleExtensionTypeNames[extension_type],
107110
"--force",
108111
"--yes"]
109112
utils.call_subprocess_raise_output(command)
@@ -177,3 +180,31 @@ def upgrade_vme(
177180

178181
if (not deployment):
179182
raise CLIError(consts.UPGRADE_TIMEOUT_MSG.format(wait_timeout))
183+
184+
185+
def list_vme(
186+
cmd,
187+
resource_group_name: str,
188+
cluster_name: str):
189+
subscription_id = get_subscription_id(cmd.cli_ctx)
190+
191+
# Check whether the cluster exists
192+
resources = cf_resources(cmd.cli_ctx, subscription_id)
193+
cluster_resource_id = '/subscriptions/{0}/resourceGroups/{1}/providers/{2}/{3}/{4}'.format(
194+
subscription_id, resource_group_name, consts.CONNECTEDCLUSTER_RP, consts.CONNECTEDCLUSTER_TYPE, cluster_name)
195+
resources.get_by_id(cluster_resource_id, '2024-12-01-preview')
196+
197+
results = []
198+
extension_names = consts.BundleExtensionNames
199+
for extension_name in extension_names:
200+
extension_resource_id = (
201+
f"{cluster_resource_id}/Providers/Microsoft.KubernetesConfiguration/"
202+
f"extensions/{extension_name}"
203+
)
204+
try:
205+
ext = resources.get_by_id(extension_resource_id, '2022-11-01')
206+
results.append(ext)
207+
except ResourceNotFoundError:
208+
continue
209+
210+
return results

src/vme/azext_vme/utils.py

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,36 @@
66
import shutil
77
import subprocess
88
import time
9+
import sys
910
from azure.core.exceptions import ResourceNotFoundError
1011
from knack.util import CLIError
1112
from knack.log import get_logger
1213
from azext_vme import consts
13-
14+
import threading
1415

1516
logger = get_logger(__name__)
1617

1718

18-
def call_subprocess_raise_output(cmd: list, logcmd: bool = True) -> str:
19+
class PollingAnimation:
20+
def __init__(self):
21+
self.tickers = ["/", "|", "\\", "-", "/", "|", "\\", "-"]
22+
self.currTicker = 0
23+
self.running = True
24+
25+
def tick(self):
26+
while self.running: # Keep the animation going
27+
sys.stdout.write('\r' + self.tickers[self.currTicker] + " Running ..")
28+
sys.stdout.flush()
29+
self.currTicker = (self.currTicker + 1) % len(self.tickers)
30+
time.sleep(0.5) # Adjust speed if needed
31+
32+
def flush(self):
33+
self.running = False # Stop the animation
34+
sys.stdout.write("\r\033[K") # Clears the line
35+
sys.stdout.flush()
36+
37+
38+
def call_subprocess_raise_output(cmd: list, logcmd: bool = False, logstatus: bool = True) -> str:
1939
"""
2040
Call a subprocess and raise a CLIError with the output if it fails.
2141
@@ -32,6 +52,11 @@ def call_subprocess_raise_output(cmd: list, logcmd: bool = True) -> str:
3252
# Log the command to be run, but do not log the password.
3353
print(f"Running command: {' '.join(log_cmd)}")
3454

55+
if logstatus:
56+
animation = PollingAnimation()
57+
spinner_thread = threading.Thread(target=animation.tick)
58+
spinner_thread.start()
59+
3560
try:
3661
called_process = subprocess.run(
3762
cmd, encoding="utf-8", capture_output=True, text=True, check=True
@@ -43,15 +68,21 @@ def call_subprocess_raise_output(cmd: list, logcmd: bool = True) -> str:
4368
called_process.stderr,
4469
)
4570

71+
if logstatus:
72+
animation.running = False
73+
spinner_thread.join()
74+
animation.flush()
4675
return called_process.stdout
4776
except subprocess.CalledProcessError as error:
77+
if logstatus:
78+
animation.running = False
79+
spinner_thread.join()
80+
animation.flush()
4881
all_output: str = (
4982
f"Command: {' '.join(log_cmd)}\n"
50-
f"stdout: {error.stdout}\n"
51-
f"stderr: {error.stderr}\n"
52-
f"Return code: {error.returncode}"
83+
f"{error.stderr}"
5384
)
54-
logger.debug("The following command failed to run:\n%s", all_output)
85+
5586
# Raise the error without the original exception, which may contain secrets.
5687
raise CLIError(all_output) from None
5788

@@ -89,10 +120,10 @@ def check_and_add_cli_extension(cli_extension_name):
89120
"-o",
90121
"tsv"
91122
]
92-
result = call_subprocess_raise_output(command, False)
123+
result = call_subprocess_raise_output(command, False, False)
93124

94125
if not (cli_extension_name in result.strip()):
95-
print(f"{cli_extension_name} is not installed. Adding it now...")
126+
print(f"Installing az cli extension {cli_extension_name}...")
96127
command = [
97128
str(shutil.which("az")),
98129
"extension",
@@ -101,6 +132,7 @@ def check_and_add_cli_extension(cli_extension_name):
101132
cli_extension_name
102133
]
103134
call_subprocess_raise_output(command)
135+
print(f"Installed az cli extension {cli_extension_name} successfully.")
104136

105137

106138
def check_and_enable_bundle_feature_flag(
@@ -131,7 +163,8 @@ def check_and_enable_bundle_feature_flag(
131163
call_subprocess_raise_output(command)
132164

133165
# Wait for the feature flag to be enabled on the dp side
134-
time.sleep(5)
166+
time.sleep(30)
167+
print("Enabled the bundle feature flag successfully.")
135168

136169

137170
def check_deployment_status(resources, deployment, timestamp):

src/vme/setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from setuptools import setup, find_packages
1111

1212
# HISTORY.rst entry.
13-
VERSION = '1.0.0b1'
13+
VERSION = '1.0.1b1'
1414

1515
# The full list of classifiers is available at
1616
# https://pypi.python.org/pypi?%3Aaction=list_classifiers

0 commit comments

Comments
 (0)