Make -u required in CLI when required by repo

This patch detects when a repository URL requires username substitution
and raises an exception when no username was specified.

Change-Id: Ia60982ecddd957cff8709118b3eb8a905258dd06
This commit is contained in:
Lev Morgan 2019-04-01 15:01:52 -05:00 committed by Stacey Fletcher
parent 652c3abefd
commit b8733ea7ec
5 changed files with 66 additions and 19 deletions

View File

@ -195,7 +195,7 @@ The SSH public key to use when cloning remote authenticated repositories.
Required for cloning repositories via SSH protocol. Required for cloning repositories via SSH protocol.
**-u / --repo-username** (Optional, SSH only). **-u / --repo-username** (Optional, unless required by repo URL).
The SSH username to use when cloning remote authenticated repositories The SSH username to use when cloning remote authenticated repositories
specified in the site-definition file. Any occurrences of ``REPO_USERNAME`` specified in the site-definition file. Any occurrences of ``REPO_USERNAME``
@ -203,6 +203,8 @@ in an entry under the ``repositories`` field in a given
:file:`site-definition.yaml` will be replaced with this value. :file:`site-definition.yaml` will be replaced with this value.
Required for cloning repositories via SSH protocol. Required for cloning repositories via SSH protocol.
This argument will generate an exception if no repo URL
uses ``REPO_USERNAME``.
Examples Examples
^^^^^^^^ ^^^^^^^^

View File

@ -81,7 +81,8 @@ REPOSITORY_USERNAME_OPTION = click.option(
help='The SSH username to use when cloning remote authenticated ' help='The SSH username to use when cloning remote authenticated '
'repositories specified in the site-definition file. Any ' 'repositories specified in the site-definition file. Any '
'occurrences of REPO_USERNAME will be replaced with this ' 'occurrences of REPO_USERNAME will be replaced with this '
'value.') 'value.\n'
'Use only if REPO_USERNAME appears in a repo URL.')
REPOSITORY_CLONE_PATH_OPTION = click.option( REPOSITORY_CLONE_PATH_OPTION = click.option(
'-p', '-p',

View File

@ -67,6 +67,11 @@ class GitInvalidRepoException(PeglegBaseException):
message = 'The repository path or URL is invalid: %(repo_path)s' message = 'The repository path or URL is invalid: %(repo_path)s'
class GitMissingUserException(PeglegBaseException):
"""Exception raised when a username is required, but not provided."""
message = 'Repo URL %(url)s reuqires a username, but none was provided.'
# #
# PKI EXCEPTIONS # PKI EXCEPTIONS
# #

View File

@ -327,6 +327,10 @@ def _format_url_with_repo_username(repo_url_or_path):
else: else:
repo_url_or_path = repo_url_or_path.replace( repo_url_or_path = repo_url_or_path.replace(
'REPO_USERNAME', repo_user) 'REPO_USERNAME', repo_user)
elif "REPO_USERNAME" in repo_url_or_path:
raise exceptions.GitMissingUserException(url=repo_url_or_path)
return repo_url_or_path return repo_url_or_path

View File

@ -12,10 +12,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import copy
import mock import mock
import os import os
import yaml
import click import click
import pytest import pytest
@ -25,6 +23,8 @@ from pegleg.engine import exceptions
from pegleg.engine import repository from pegleg.engine import repository
from pegleg.engine import util from pegleg.engine import util
REPO_USERNAME="test_username"
TEST_REPOSITORIES = { TEST_REPOSITORIES = {
'repositories': { 'repositories': {
'global': { 'global': {
@ -40,6 +40,23 @@ TEST_REPOSITORIES = {
} }
} }
FORMATTED_REPOSITORIES = {
'repositories': {
'global': {
'revision': '843d1a50106e1f17f3f722e2ef1634ae442fe68f',
'url': 'ssh://{}@gerrit:29418/aic-clcp-manifests.git'.format(
REPO_USERNAME)
},
'secrets': {
'revision':
'master',
'url': ('ssh://{}@gerrit:29418/aic-clcp-security-'
'manifests.git'.format(REPO_USERNAME))
}
}
}
config.set_repo_username(REPO_USERNAME)
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def clean_temp_folders(): def clean_temp_folders():
@ -129,7 +146,7 @@ def _test_process_repositories(site_repo=None,
] ]
mock_calls.extend([ mock_calls.extend([
mock.call(r['url'], ref=r['revision'], auth_key=None) mock.call(r['url'], ref=r['revision'], auth_key=None)
for r in TEST_REPOSITORIES['repositories'].values() for r in FORMATTED_REPOSITORIES['repositories'].values()
]) ])
m_clone_repo.assert_has_calls(mock_calls) m_clone_repo.assert_has_calls(mock_calls)
elif repo_username: elif repo_username:
@ -140,7 +157,7 @@ def _test_process_repositories(site_repo=None,
r['url'].replace('REPO_USERNAME', repo_username), r['url'].replace('REPO_USERNAME', repo_username),
ref=r['revision'], ref=r['revision'],
auth_key=None) auth_key=None)
for r in TEST_REPOSITORIES['repositories'].values() for r in FORMATTED_REPOSITORIES['repositories'].values()
]) ])
elif repo_overrides: elif repo_overrides:
# This is computed from: len(cloned extra repos) + # This is computed from: len(cloned extra repos) +
@ -148,7 +165,7 @@ def _test_process_repositories(site_repo=None,
expected_call_count = len(TEST_REPOSITORIES['repositories']) + 1 expected_call_count = len(TEST_REPOSITORIES['repositories']) + 1
assert (expected_call_count == m_clone_repo.call_count) assert (expected_call_count == m_clone_repo.call_count)
for x, r in TEST_REPOSITORIES['repositories'].items(): for x, r in FORMATTED_REPOSITORIES['repositories'].items():
if x in expected_repo_overrides: if x in expected_repo_overrides:
repo_url = expected_repo_overrides[x]['url'] repo_url = expected_repo_overrides[x]['url']
ref = expected_repo_overrides[x]['revision'] ref = expected_repo_overrides[x]['revision']
@ -161,7 +178,7 @@ def _test_process_repositories(site_repo=None,
else: else:
m_clone_repo.assert_has_calls([ m_clone_repo.assert_has_calls([
mock.call(r['url'], ref=r['revision'], auth_key=None) mock.call(r['url'], ref=r['revision'], auth_key=None)
for r in TEST_REPOSITORIES['repositories'].values() for r in FORMATTED_REPOSITORIES['repositories'].values()
]) ])
if site_repo: if site_repo:
@ -195,19 +212,13 @@ def test_process_repositories():
def test_process_repositories_with_site_repo_url(): def test_process_repositories_with_site_repo_url():
"""Test process_repository when site repo is a remote URL.""" """Test process_repository when site repo is a remote URL."""
# With REPO_USERNAME.
site_repo = (
'ssh://REPO_USERNAME@gerrit:29418/aic-clcp-site-manifests.git@333')
_test_process_repositories(
site_repo=site_repo,
expected_repo_url=(
"ssh://REPO_USERNAME@gerrit:29418/aic-clcp-site-manifests"),
expected_repo_revision="333")
# Without REPO_USERNAME. # Without REPO_USERNAME.
site_repo = 'ssh://gerrit:29418/aic-clcp-site-manifests.git@333' site_repo = 'ssh://{}:29418/aic-clcp-site-manifests.git@333'.format(
REPO_USERNAME)
_test_process_repositories( _test_process_repositories(
site_repo=site_repo, site_repo=site_repo,
expected_repo_url="ssh://gerrit:29418/aic-clcp-site-manifests", expected_repo_url="ssh://{}:29418/aic-clcp-site-manifests".format(
REPO_USERNAME),
expected_repo_revision="333") expected_repo_revision="333")
@ -257,7 +268,8 @@ def test_process_repositories_with_repo_overrides_remote_urls():
} }
expected_repo_overrides = { expected_repo_overrides = {
'global': { 'global': {
'url': 'ssh://REPO_USERNAME@gerrit:29418/aic-clcp-manifests', 'url': 'ssh://{}@gerrit:29418/aic-clcp-manifests'.format(
REPO_USERNAME),
'revision': '12345' 'revision': '12345'
}, },
} }
@ -515,3 +527,26 @@ def test_process_site_repository(_):
_do_test( _do_test(
'ssh://foo@opendev.org/airship/treasuremap:12345', 'ssh://foo@opendev.org/airship/treasuremap:12345',
expected='ssh://foo@opendev.org/airship/treasuremap:12345') expected='ssh://foo@opendev.org/airship/treasuremap:12345')
def test_format_url_with_repo_username():
TEST_URL = 'ssh://REPO_USERNAME@gerrit:29418/airship/pegleg'
with mock.patch.object(
config,
'get_repo_username',
autospec=True,
return_value=REPO_USERNAME):
res = repository._format_url_with_repo_username(TEST_URL)
assert res == 'ssh://{}@gerrit:29418/airship/pegleg'.format(
REPO_USERNAME)
with mock.patch.object(
config,
'get_repo_username',
autospec=True,
return_value=''):
pytest.raises(
exceptions.GitMissingUserException,
repository._format_url_with_repo_username,
TEST_URL)