Skip to content

Commit

Permalink
Update CLI integration test (#3898)
Browse files Browse the repository at this point in the history
* change integration cli test

* fix name prefix
  • Loading branch information
bitoku authored Oct 11, 2024
1 parent 96637db commit 53673d4
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 22 deletions.
119 changes: 119 additions & 0 deletions python/az/aro/azext_aro/tests/latest/custom_preparers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the Apache License 2.0.

from azure.cli.testsdk.preparers import RoleBasedServicePrincipalPreparer
from azure.cli.testsdk.scenario_tests.utilities import is_text_payload
from azure.cli.testsdk.utilities import GraphClientPasswordReplacer
from azure.mgmt.core.tools import resource_id

MOCK_GUID = '00000000-0000-0000-0000-000000000001'
MOCK_SECRET = 'fake-secret'


class AROClusterServicePrincipalPreparer(RoleBasedServicePrincipalPreparer):
def __init__(
self,
name_prefix="clitest",
skip_assignment=True,
parameter_name="client_id",
parameter_password="client_secret",
dev_setting_sp_name="AZURE_CLI_TEST_DEV_SP_NAME",
dev_setting_sp_password="AZURE_CLI_TEST_DEV_SP_PASSWORD",
key="aro_csp",
):
super(AROClusterServicePrincipalPreparer, self).__init__(
name_prefix,
skip_assignment,
parameter_name,
parameter_password,
dev_setting_sp_name,
dev_setting_sp_password,
key,
)
self.client_id_to_replace = None
self.client_secret_to_replace = None

def create_resource(self, name, **kwargs):
client_id, client_secret = self._get_csp_credentials(name)

self.test_class_instance.kwargs[self.key] = client_id
self.test_class_instance.kwargs["{}_pass".format(self.key)] = client_secret

return {
self.parameter_name: client_id,
self.parameter_password: client_secret,
}

# Overriden because RoleBasedServicePrincipal.remove_resource does not delete
# the underlying AAD application generated when creating the service principal
def remove_resource(self, name, **kwargs):
super().remove_resource(name, **kwargs)

if not self.dev_setting_sp_name:
self.live_only_execute(self.cli_ctx, 'az ad app delete --id {}'.format(self.result.get('appId')))

def process_request(self, request):
if self.client_id_to_replace in request.uri:
request.uri = request.uri.replace(self.client_id_to_replace, MOCK_GUID)

if is_text_payload(request) and isinstance(request.body, bytes):
request.body = self._replace_byte_keys(request.body)
elif is_text_payload(request) and isinstance(request.body, str):
request.body = self._replace_string_keys(request.body)

return request

def process_response(self, response):
if is_text_payload(response) and response['body']['string']:
response['body']['string'] = self._replace_string_keys(response['body']['string'])

return response

def _get_csp_credentials(self, name):
if not self.live_test and not self.test_class_instance.in_recording:
return MOCK_GUID, MOCK_SECRET

client_id, client_secret = self._generate_csp(name)

# call AbstractPreparer.moniker to make resource counts and self.resource_moniker consistent between live
# and play-back. see SingleValueReplacer.process_request, AbstractPreparer.__call__._preparer_wrapper
# and ScenarioTest.create_random_name. This is so that when self.create_random_name is called for the
# first time during live or playback, it would have the same value.
# In short, the default sp preparer in live mode does not call moniker, which leads to inconsistent counts.
_ = self.moniker

self.client_id_to_replace = client_id
self.client_secret_to_replace = client_secret

return client_id, client_secret

def _generate_csp(self, name):
if self.dev_setting_sp_name:
client_id = self.dev_setting_sp_name
client_secret = self.dev_setting_sp_password

return client_id, client_secret

subscription = self.test_class_instance.get_subscription_id()
resource_group = self.test_class_instance.kwargs.get('rg')
command = 'az ad sp create-for-rbac -n {} --role contributor --scopes "{}"'\
.format(name, resource_id(subscription=subscription, resource_group=resource_group))

try:
self.result = self.live_only_execute(self.cli_ctx, command).get_output_in_json()
except AttributeError:
pass

client_id = self.result['appId']
client_secret = self.result.get('password') or GraphClientPasswordReplacer.PWD_REPLACEMENT

return client_id, client_secret

def _replace_string_keys(self, val):
if self.client_id_to_replace is None:
return val

return val.replace(self.client_id_to_replace, MOCK_GUID).replace(self.client_secret_to_replace, MOCK_SECRET)

def _replace_byte_keys(self, val):
return self._replace_string_keys(val.decode('utf-8')).encode('utf-8')
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,93 @@
# Licensed under the Apache License 2.0.

import os
from random import randint
from unittest import mock

from azure_devtools.scenario_tests import AllowLargeResponse
from azure.cli.testsdk import ResourceGroupPreparer
from azure.cli.testsdk import ScenarioTest

from knack.log import get_logger
from azext_aro.tests.latest.custom_preparers import AROClusterServicePrincipalPreparer
from azure.cli.testsdk import ScenarioTest, ResourceGroupPreparer
from azure.cli.testsdk.checkers import StringContainCheck
from azure.cli.testsdk.scenario_tests import AllowLargeResponse

TEST_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), '..'))
logger = get_logger(__name__)


class AroScenarioTest(ScenarioTest):
@ResourceGroupPreparer(name_prefix='cli_test_aro')
def test_aro(self, resource_group):
class AroScenarioTests(ScenarioTest):
@AllowLargeResponse()
@ResourceGroupPreparer(random_name_length=28, name_prefix='cli_test_aro', location='eastus')
@AROClusterServicePrincipalPreparer(name_prefix='cli_test_aro')
def test_aro_public_cluster(self, resource_group):
from azure.mgmt.core.tools import resource_id

subscription = self.get_subscription_id()

master_subnet = self.create_random_name('dev_master', 14)
worker_subnet = self.create_random_name('dev_worker', 14)
name = self.create_random_name('aro', 14)

temp_kubeconfig_path = self.create_random_name('kubeconfig', 24) + '.tmp'

self.kwargs.update({
'name': 'test1'
'name': name,
'resource_group': resource_group,
'subscription': subscription,
'master_subnet': master_subnet,
'worker_subnet': worker_subnet,
'master_ip_range': '10.{}.{}.0/24'.format(randint(0, 127), randint(0, 255)),
'worker_ip_range': '10.{}.{}.0/24'.format(randint(0, 127), randint(0, 255)),
'master_subnet_resource': resource_id(subscription=subscription, resource_group=resource_group, namespace='Microsoft.Network', type='virtualNetworks', child_type_1='subnets', name='dev-vnet', child_name_1=master_subnet),
'worker_subnet_resource': resource_id(subscription=subscription, resource_group=resource_group, namespace='Microsoft.Network', type='virtualNetworks', child_type_1='subnets', name='dev-vnet', child_name_1=worker_subnet),
'temp_kubeconfig_path': temp_kubeconfig_path,
})

# test aro create
self.cmd('network vnet create -g {rg} -n dev-vnet --address-prefixes 10.0.0.0/9')
self.cmd('network vnet subnet create -g {rg} --vnet-name dev-vnet -n {master_subnet} --address-prefixes {master_ip_range} --service-endpoints Microsoft.ContainerRegistry --default-outbound false')
self.cmd('network vnet subnet create -g {rg} --vnet-name dev-vnet -n {worker_subnet} --address-prefixes {worker_ip_range} --service-endpoints Microsoft.ContainerRegistry --default-outbound false')
self.cmd('network vnet subnet update -g {rg} --vnet-name dev-vnet -n {master_subnet} --private-link-service-network-policies Disabled')

# aro validate
with mock.patch('azure.cli.command_modules.aro._rbac._gen_uuid', side_effect=self.create_guid):
self.cmd('aro create -g {rg} -n {name} --tags foo=doo', checks=[
self.check('tags.foo', 'doo'),
self.check('name', '{name}')
self.cmd('aro validate -g {rg} -n {name} --client-id {aro_csp} --client-secret {aro_csp_pass} --master-subnet {master_subnet_resource} --worker-subnet {worker_subnet_resource} --subscription {subscription}')

# aro create
with mock.patch('azure.cli.command_modules.aro._rbac._gen_uuid', side_effect=self.create_guid):
self.cmd('aro create -g {rg} -n {name} --client-id {aro_csp} --client-secret {aro_csp_pass} --master-subnet {master_subnet_resource} --worker-subnet {worker_subnet_resource} --subscription {subscription} --tags test=create', checks=[
self.check('tags.test', 'create'),
self.check('name', '{name}'),
self.check('masterProfile.subnetId', '{master_subnet_resource}'),
self.check('workerProfiles[0].subnetId', '{worker_subnet_resource}'),
self.check('provisioningState', 'Succeeded')
])

self.cmd('aro update -g {rg} -n {name} --tags foo=boo', checks=[
self.check('tags.foo', 'boo')
# aro list-credentials
self.cmd('aro list-credentials -g {rg} -n {name} --subscription {subscription}', checks=[self.check('kubeadminUsername', 'kubeadmin')])

# aro get-admin-kubeconfig
try:
self.cmd('aro get-admin-kubeconfig -g {rg} -n {name} --subscription {subscription} -f {temp_kubeconfig_path}')
self.assertGreater(os.path.getsize(temp_kubeconfig_path), 0)
finally:
os.remove(temp_kubeconfig_path)

# aro show
self.cmd('aro show -g {rg} -n {name} --subscription {subscription} --output table', checks=[
StringContainCheck(name),
StringContainCheck(resource_group),
StringContainCheck('eastus'),
StringContainCheck('Succeeded'),
])

count = len(self.cmd('aro list').get_output_in_json())
self.cmd('aro show -g {rg} -n {name}', checks=[
self.check('name', '{name}'),
self.check('resourceGroup', '{rg}'),
self.check('tags.foo', 'boo')
# aro list
self.cmd('aro list -g {rg} --subscription {subscription}', checks=[
self.check('[0].name', '{name}'),
self.check('[0].provisioningState', 'Succeeded'),
self.check_pattern('[0].id', '.*{name}')
])

self.cmd('aro delete -g {rg} -n {name}')
# aro update
self.cmd('aro update -g {rg} -n {name} --subscription {subscription}', expect_failure=False)

final_count = len(self.cmd('aro list').get_output_in_json())
self.assertTrue(final_count, count - 1)
# aro delete
self.cmd('aro delete -y -g {rg} -n {name} --subscription {subscription}', expect_failure=False)

0 comments on commit 53673d4

Please sign in to comment.