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

Update CLI integration test #3898

Merged
merged 2 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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)),
tsatam marked this conversation as resolved.
Show resolved Hide resolved
'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)
Loading