Skip to content
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
1 change: 1 addition & 0 deletions src/azure-cli/azure/cli/command_modules/acs/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,7 @@ def load_arguments(self, _):
c.argument('kubelogin_version', validator=validate_kubelogin_version, help='Version of kubelogin to install.')
c.argument('kubelogin_install_location', default=_get_default_install_location('kubelogin'), help='Path at which to install kubelogin. Note: the path should contain the binary filename.')
c.argument('kubelogin_base_src_url', options_list=['--kubelogin-base-src-url', '-l'], help='Base download source URL for kubelogin releases.')
c.argument('gh_token', help='GitHub authentication token used when downloading kubelogin binaries from GitHub releases. Supplying a token helps avoid GitHub API rate limits.')

with self.argument_context('aks update-credentials', arg_group='Service Principal') as c:
c.argument('reset_service_principal', action='store_true')
Expand Down
20 changes: 12 additions & 8 deletions src/azure-cli/azure/cli/command_modules/acs/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -2239,12 +2239,12 @@ def aks_check_acr(cmd, client, resource_group_name, name, acr, node_name=None):
# install kubectl & kubelogin
def k8s_install_cli(cmd, client_version='latest', install_location=None, base_src_url=None,
kubelogin_version='latest', kubelogin_install_location=None,
kubelogin_base_src_url=None):
kubelogin_base_src_url=None, gh_token=None):
arch = get_arch_for_cli_binary()
k8s_install_kubectl(cmd, client_version,
install_location, base_src_url, arch=arch)
k8s_install_kubelogin(cmd, kubelogin_version,
kubelogin_install_location, kubelogin_base_src_url, arch=arch)
kubelogin_install_location, kubelogin_base_src_url, arch=arch, gh_token=gh_token)


# determine the architecture for the binary based on platform.machine()
Expand Down Expand Up @@ -2444,7 +2444,7 @@ def k8s_install_kubectl(cmd, client_version='latest', install_location=None, sou


# install kubelogin
def k8s_install_kubelogin(cmd, client_version='latest', install_location=None, source_url=None, arch=None):
def k8s_install_kubelogin(cmd, client_version='latest', install_location=None, source_url=None, arch=None, gh_token=None):
Comment thread
FumingZhang marked this conversation as resolved.
"""
Install kubelogin, a client-go credential (exec) plugin implementing azure authentication.
Comment thread
FumingZhang marked this conversation as resolved.
"""
Expand All @@ -2462,7 +2462,7 @@ def k8s_install_kubelogin(cmd, client_version='latest', install_location=None, s
latest_release_url = 'https://mirror.azure.cn/kubernetes/kubelogin/latest'
logger.warning(
'No version specified, will get the latest version of kubelogin from "%s"', latest_release_url)
latest_release = _urlopen_read(latest_release_url)
latest_release = _urlopen_read(latest_release_url, gh_token=gh_token)
client_version = json.loads(latest_release)['tag_name'].strip()
else:
client_version = "v%s" % client_version
Expand Down Expand Up @@ -2519,11 +2519,15 @@ def _ssl_context():
return ssl.create_default_context()


def _urlopen_read(url, context=None):
def _urlopen_read(url, context=None, gh_token=None):
if context is None:
context = _ssl_context()
try:
return urlopen(url, context=context).read()
from urllib.request import Request
request = Request(url)
if gh_token:
request.add_header('Authorization', f'Bearer {gh_token}')
return urlopen(request, context=context).read()
except URLError as ex:
error_msg = str(ex)
if "[SSL: CERTIFICATE_VERIFY_FAILED]" in error_msg and "unable to get local issuer certificate" in error_msg:
Expand All @@ -2535,9 +2539,9 @@ def _urlopen_read(url, context=None):
raise ex


def _urlretrieve(url, filename):
def _urlretrieve(url, filename, gh_token=None):
with open(filename, "wb") as f:
f.write(_urlopen_read(url))
f.write(_urlopen_read(url, gh_token=gh_token))


def _unzip(src, dest):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,40 @@ def test_k8s_install_kubelogin_with_custom_source_url(self, logger_mock, mock_ur
finally:
shutil.rmtree(temp_dir)

@mock.patch('azure.cli.command_modules.acs.custom._urlopen_read')
@mock.patch('azure.cli.command_modules.acs.custom._urlretrieve')
@mock.patch('azure.cli.command_modules.acs.custom.logger')
def test_k8s_install_kubelogin_with_gh_token(self, logger_mock, mock_url_retrieve, mock_urlopen_read):
"""Test that gh_token parameter is properly passed to HTTP requests when installing kubelogin."""
# Mock the GitHub API response for latest release
mock_urlopen_read.return_value = b'{"tag_name": "v0.0.30"}'
# Mock the zip file download
mock_url_retrieve.side_effect = create_kubelogin_zip

try:
temp_dir = tempfile.mkdtemp()
test_location = os.path.join(temp_dir, 'foo', 'kubelogin')
test_gh_token = 'ghp_test_token_123'

# Install kubelogin with gh_token
k8s_install_kubelogin(
mock.MagicMock(),
client_version='latest',
install_location=test_location,
arch="amd64",
gh_token=test_gh_token
)

# Verify gh_token was passed to _urlopen_read for GitHub API call
mock_urlopen_read.assert_called_once()
call_args = mock_urlopen_read.call_args
self.assertEqual(call_args.kwargs.get('gh_token'), test_gh_token)

# Verify the installation completed
self.assertTrue(os.path.exists(test_location))
finally:
shutil.rmtree(temp_dir)

@mock.patch('azure.cli.command_modules.acs.addonconfiguration.get_rg_location', return_value='eastus')
@mock.patch('azure.cli.command_modules.acs.addonconfiguration.get_resource_groups_client', autospec=True)
@mock.patch('azure.cli.command_modules.acs.addonconfiguration.get_resources_client', autospec=True)
Expand Down