@ -24,12 +24,24 @@ import ansible.module_utils.six.moves.urllib.error as urllib_error
from ansible import context
from ansible import context
from ansible . cli . galaxy import GalaxyCLI
from ansible . cli . galaxy import GalaxyCLI
from ansible . errors import AnsibleError
from ansible . errors import AnsibleError
from ansible . galaxy import collection , api
from ansible . galaxy import collection , api , dependency_resolution
from ansible . galaxy . dependency_resolution . dataclasses import Candidate , Requirement
from ansible . module_utils . _text import to_bytes , to_native , to_text
from ansible . module_utils . _text import to_bytes , to_native , to_text
from ansible . utils import context_objects as co
from ansible . utils import context_objects as co
from ansible . utils . display import Display
from ansible . utils . display import Display
class RequirementCandidates ( ) :
def __init__ ( self ) :
self . candidates = [ ]
def func_wrapper ( self , func ) :
def run ( * args , * * kwargs ) :
self . candidates = func ( * args , * * kwargs )
return self . candidates
return run
def call_galaxy_cli ( args ) :
def call_galaxy_cli ( args ) :
orig = co . GlobalCLIArgs . _Singleton__instance
orig = co . GlobalCLIArgs . _Singleton__instance
co . GlobalCLIArgs . _Singleton__instance = None
co . GlobalCLIArgs . _Singleton__instance = None
@ -160,16 +172,14 @@ def galaxy_server():
def test_build_requirement_from_path ( collection_artifact ) :
def test_build_requirement_from_path ( collection_artifact ) :
actual = collection . CollectionRequirement . from_path ( collection_artifact [ 0 ] , True )
tmp_path = os . path . join ( os . path . split ( collection_artifact [ 1 ] ) [ 0 ] , b ' temp ' )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( tmp_path , validate_certs = False )
actual = Requirement . from_dir_path_as_unknown ( collection_artifact [ 0 ] , concrete_artifact_cm )
assert actual . namespace == u ' ansible_namespace '
assert actual . namespace == u ' ansible_namespace '
assert actual . name == u ' collection '
assert actual . name == u ' collection '
assert actual . b_path == collection_artifact [ 0 ]
assert actual . src == collection_artifact [ 0 ]
assert actual . api is None
assert actual . ver == u ' 0.1.0 '
assert actual . skip is True
assert actual . versions == set ( [ u ' * ' ] )
assert actual . latest_version == u ' * '
assert actual . dependencies == { }
@pytest.mark.parametrize ( ' version ' , [ ' 1.1.1 ' , ' 1.1.0 ' , ' 1.0.0 ' ] )
@pytest.mark.parametrize ( ' version ' , [ ' 1.1.1 ' , ' 1.1.0 ' , ' 1.0.0 ' ] )
@ -188,17 +198,15 @@ def test_build_requirement_from_path_with_manifest(version, collection_artifact)
with open ( manifest_path , ' wb ' ) as manifest_obj :
with open ( manifest_path , ' wb ' ) as manifest_obj :
manifest_obj . write ( to_bytes ( manifest_value ) )
manifest_obj . write ( to_bytes ( manifest_value ) )
actual = collection . CollectionRequirement . from_path ( collection_artifact [ 0 ] , True )
tmp_path = os . path . join ( os . path . split ( collection_artifact [ 1 ] ) [ 0 ] , b ' temp ' )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( tmp_path , validate_certs = False )
actual = Requirement . from_dir_path_as_unknown ( collection_artifact [ 0 ] , concrete_artifact_cm )
# While the folder name suggests a different collection, we treat MANIFEST.json as the source of truth.
# While the folder name suggests a different collection, we treat MANIFEST.json as the source of truth.
assert actual . namespace == u ' namespace '
assert actual . namespace == u ' namespace '
assert actual . name == u ' name '
assert actual . name == u ' name '
assert actual . b_path == collection_artifact [ 0 ]
assert actual . src == collection_artifact [ 0 ]
assert actual . api is None
assert actual . ver == to_text ( version )
assert actual . skip is True
assert actual . versions == set ( [ to_text ( version ) ] )
assert actual . latest_version == to_text ( version )
assert actual . dependencies == { ' ansible_namespace.collection ' : ' * ' }
def test_build_requirement_from_path_invalid_manifest ( collection_artifact ) :
def test_build_requirement_from_path_invalid_manifest ( collection_artifact ) :
@ -206,12 +214,19 @@ def test_build_requirement_from_path_invalid_manifest(collection_artifact):
with open ( manifest_path , ' wb ' ) as manifest_obj :
with open ( manifest_path , ' wb ' ) as manifest_obj :
manifest_obj . write ( b " not json " )
manifest_obj . write ( b " not json " )
expected = " Collection file at ' %s ' does not contain a valid json string. " % to_native ( manifest_path )
tmp_path = os . path . join ( os . path . split ( collection_artifact [ 1 ] ) [ 0 ] , b ' temp ' )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( tmp_path , validate_certs = False )
expected = " Collection tar file member MANIFEST.json does not contain a valid json string. "
with pytest . raises ( AnsibleError , match = expected ) :
with pytest . raises ( AnsibleError , match = expected ) :
collection . CollectionRequirement . from_path ( collection_artifact [ 0 ] , True )
Requirement. from_ dir_ path_as_unknown ( collection_artifact [ 0 ] , concrete_artifact_cm )
def test_build_requirement_from_path_no_version ( collection_artifact , monkeypatch ) :
def test_build_artifact_from_path_no_version ( collection_artifact , monkeypatch ) :
mock_display = MagicMock ( )
monkeypatch . setattr ( Display , ' display ' , mock_display )
# a collection artifact should always contain a valid version
manifest_path = os . path . join ( collection_artifact [ 0 ] , b ' MANIFEST.json ' )
manifest_path = os . path . join ( collection_artifact [ 0 ] , b ' MANIFEST.json ' )
manifest_value = json . dumps ( {
manifest_value = json . dumps ( {
' collection_info ' : {
' collection_info ' : {
@ -224,40 +239,56 @@ def test_build_requirement_from_path_no_version(collection_artifact, monkeypatch
with open ( manifest_path , ' wb ' ) as manifest_obj :
with open ( manifest_path , ' wb ' ) as manifest_obj :
manifest_obj . write ( to_bytes ( manifest_value ) )
manifest_obj . write ( to_bytes ( manifest_value ) )
tmp_path = os . path . join ( os . path . split ( collection_artifact [ 1 ] ) [ 0 ] , b ' temp ' )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( tmp_path , validate_certs = False )
expected = (
' ^Collection metadata file at `.*` is expected to have a valid SemVer '
' version value but got {empty_unicode_string!r} $ ' .
format ( empty_unicode_string = u ' ' )
)
with pytest . raises ( AnsibleError , match = expected ) :
Requirement . from_dir_path_as_unknown ( collection_artifact [ 0 ] , concrete_artifact_cm )
def test_build_requirement_from_path_no_version ( collection_artifact , monkeypatch ) :
mock_display = MagicMock ( )
mock_display = MagicMock ( )
monkeypatch . setattr ( Display , ' display ' , mock_display )
monkeypatch . setattr ( Display , ' display ' , mock_display )
actual = collection . CollectionRequirement . from_path ( collection_artifact [ 0 ] , True )
# version may be falsey/arbitrary strings for collections in development
manifest_path = os . path . join ( collection_artifact [ 0 ] , b ' galaxy.yml ' )
metadata = {
' authors ' : [ ' Ansible ' ] ,
' readme ' : ' README.md ' ,
' namespace ' : ' namespace ' ,
' name ' : ' name ' ,
' version ' : ' ' ,
' dependencies ' : { } ,
}
with open ( manifest_path , ' wb ' ) as manifest_obj :
manifest_obj . write ( to_bytes ( yaml . safe_dump ( metadata ) ) )
tmp_path = os . path . join ( os . path . split ( collection_artifact [ 1 ] ) [ 0 ] , b ' temp ' )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( tmp_path , validate_certs = False )
actual = Requirement . from_dir_path_as_unknown ( collection_artifact [ 0 ] , concrete_artifact_cm )
# While the folder name suggests a different collection, we treat MANIFEST.json as the source of truth.
# While the folder name suggests a different collection, we treat MANIFEST.json as the source of truth.
assert actual . namespace == u ' namespace '
assert actual . namespace == u ' namespace '
assert actual . name == u ' name '
assert actual . name == u ' name '
assert actual . b_path == collection_artifact [ 0 ]
assert actual . src == collection_artifact [ 0 ]
assert actual . api is None
assert actual . ver == u ' * '
assert actual . skip is True
assert actual . versions == set ( [ ' * ' ] )
assert actual . latest_version == u ' * '
assert actual . dependencies == { }
assert mock_display . call_count == 1
actual_warn = ' ' . join ( mock_display . mock_calls [ 0 ] [ 1 ] [ 0 ] . split ( ' \n ' ) )
expected_warn = " Collection at ' %s ' does not have a valid version set, falling back to ' * ' . Found version: ' ' " \
% to_text ( collection_artifact [ 0 ] )
assert expected_warn in actual_warn
def test_build_requirement_from_tar ( collection_artifact ) :
def test_build_requirement_from_tar ( collection_artifact ) :
actual = collection . CollectionRequirement . from_tar ( collection_artifact [ 1 ] , True , True )
tmp_path = os . path . join ( os . path . split ( collection_artifact [ 1 ] ) [ 0 ] , b ' temp ' )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( tmp_path , validate_certs = False )
actual = Requirement . from_requirement_dict ( { ' name ' : to_text ( collection_artifact [ 1 ] ) } , concrete_artifact_cm )
assert actual . namespace == u ' ansible_namespace '
assert actual . namespace == u ' ansible_namespace '
assert actual . name == u ' collection '
assert actual . name == u ' collection '
assert actual . b_path == collection_artifact [ 1 ]
assert actual . src == to_text ( collection_artifact [ 1 ] )
assert actual . api is None
assert actual . ver == u ' 0.1.0 '
assert actual . skip is False
assert actual . versions == set ( [ u ' 0.1.0 ' ] )
assert actual . latest_version == u ' 0.1.0 '
assert actual . dependencies == { }
def test_build_requirement_from_tar_fail_not_tar ( tmp_path_factory ) :
def test_build_requirement_from_tar_fail_not_tar ( tmp_path_factory ) :
@ -266,9 +297,11 @@ def test_build_requirement_from_tar_fail_not_tar(tmp_path_factory):
with open ( test_file , ' wb ' ) as test_obj :
with open ( test_file , ' wb ' ) as test_obj :
test_obj . write ( b " \x00 \x01 \x02 \x03 " )
test_obj . write ( b " \x00 \x01 \x02 \x03 " )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( test_dir , validate_certs = False )
expected = " Collection artifact at ' %s ' is not a valid tar file. " % to_native ( test_file )
expected = " Collection artifact at ' %s ' is not a valid tar file. " % to_native ( test_file )
with pytest . raises ( AnsibleError , match = expected ) :
with pytest . raises ( AnsibleError , match = expected ) :
collection. CollectionRequirement . from_tar ( test_file , True , True )
Requirement. from_requirement_dict ( { ' name ' : to_text ( test_file ) } , concrete_artifact_cm )
def test_build_requirement_from_tar_no_manifest ( tmp_path_factory ) :
def test_build_requirement_from_tar_no_manifest ( tmp_path_factory ) :
@ -289,9 +322,11 @@ def test_build_requirement_from_tar_no_manifest(tmp_path_factory):
tar_info . mode = 0o0644
tar_info . mode = 0o0644
tfile . addfile ( tarinfo = tar_info , fileobj = b_io )
tfile . addfile ( tarinfo = tar_info , fileobj = b_io )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( test_dir , validate_certs = False )
expected = " Collection at ' %s ' does not contain the required file MANIFEST.json. " % to_native ( tar_path )
expected = " Collection at ' %s ' does not contain the required file MANIFEST.json. " % to_native ( tar_path )
with pytest . raises ( AnsibleError , match = expected ) :
with pytest . raises ( AnsibleError , match = expected ) :
collection. CollectionRequirement . from_tar ( tar_path , True , True )
Requirement. from_requirement_dict ( { ' name ' : to_text ( tar_path ) } , concrete_artifact_cm )
def test_build_requirement_from_tar_no_files ( tmp_path_factory ) :
def test_build_requirement_from_tar_no_files ( tmp_path_factory ) :
@ -311,9 +346,9 @@ def test_build_requirement_from_tar_no_files(tmp_path_factory):
tar_info . mode = 0o0644
tar_info . mode = 0o0644
tfile . addfile ( tarinfo = tar_info , fileobj = b_io )
tfile . addfile ( tarinfo = tar_info , fileobj = b_io )
expected = " Collection at ' %s ' does not contain the required file FILES.json. " % to_native ( tar_path )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( test_dir , validate_certs = False )
with pytest . raises ( AnsibleError , match = expected ) :
with pytest . raises ( KeyError , match = ' namespace ' ) :
collection. CollectionRequirement . from_tar ( tar_path , True , True )
Requirement. from_requirement_dict ( { ' name ' : to_text ( tar_path ) } , concrete_artifact_cm )
def test_build_requirement_from_tar_invalid_manifest ( tmp_path_factory ) :
def test_build_requirement_from_tar_invalid_manifest ( tmp_path_factory ) :
@ -329,95 +364,128 @@ def test_build_requirement_from_tar_invalid_manifest(tmp_path_factory):
tar_info . mode = 0o0644
tar_info . mode = 0o0644
tfile . addfile ( tarinfo = tar_info , fileobj = b_io )
tfile . addfile ( tarinfo = tar_info , fileobj = b_io )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( test_dir , validate_certs = False )
expected = " Collection tar file member MANIFEST.json does not contain a valid json string. "
expected = " Collection tar file member MANIFEST.json does not contain a valid json string. "
with pytest . raises ( AnsibleError , match = expected ) :
with pytest . raises ( AnsibleError , match = expected ) :
collection. CollectionRequirement . from_tar ( tar_path , True , True )
Requirement. from_requirement_dict ( { ' name ' : to_text ( tar_path ) } , concrete_artifact_cm )
def test_build_requirement_from_name ( galaxy_server , monkeypatch ):
def test_build_requirement_from_name ( galaxy_server , monkeypatch , tmp_path_factory ):
mock_get_versions = MagicMock ( )
mock_get_versions = MagicMock ( )
mock_get_versions . return_value = [ ' 2.1.9 ' , ' 2.1.10 ' ]
mock_get_versions . return_value = [ ' 2.1.9 ' , ' 2.1.10 ' ]
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_get_versions )
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_get_versions )
actual = collection . CollectionRequirement . from_name ( ' namespace.collection ' , [ galaxy_server ] , ' * ' , True , True )
mock_version_metadata = MagicMock (
namespace = ' namespace ' , name = ' collection ' ,
version = ' 2.1.10 ' , artifact_sha256 = ' ' , dependencies = { }
)
monkeypatch . setattr ( api . GalaxyAPI , ' get_collection_version_metadata ' , mock_version_metadata )
test_dir = to_bytes ( tmp_path_factory . mktemp ( ' test-ÅÑŚÌβŁÈ Collections Input ' ) )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( test_dir , validate_certs = False )
collections = [ ' namespace.collection ' ]
requirements_file = None
cli = GalaxyCLI ( args = [ ' ansible-galaxy ' , ' collection ' , ' install ' , collections [ 0 ] ] )
requirements = cli . _require_one_of_collections_requirements (
collections , requirements_file , artifacts_manager = concrete_artifact_cm
) [ ' collections ' ]
actual = collection . _resolve_depenency_map ( requirements , [ galaxy_server ] , concrete_artifact_cm , None , True , False ) [ ' namespace.collection ' ]
assert actual . namespace == u ' namespace '
assert actual . namespace == u ' namespace '
assert actual . name == u ' collection '
assert actual . name == u ' collection '
assert actual . b_path is None
assert actual . ver == u ' 2.1.10 '
assert actual . api == galaxy_server
assert actual . src == galaxy_server
assert actual . skip is False
assert actual . versions == set ( [ u ' 2.1.9 ' , u ' 2.1.10 ' ] )
assert actual . latest_version == u ' 2.1.10 '
assert actual . dependencies == { }
assert mock_get_versions . call_count == 1
assert mock_get_versions . call_count == 1
assert mock_get_versions . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' )
assert mock_get_versions . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' )
def test_build_requirement_from_name_with_prerelease ( galaxy_server , monkeypatch ) :
def test_build_requirement_from_name_with_prerelease ( galaxy_server , monkeypatch , tmp_path_factory ):
mock_get_versions = MagicMock ( )
mock_get_versions = MagicMock ( )
mock_get_versions . return_value = [ ' 1.0.1 ' , ' 2.0.1-beta.1 ' , ' 2.0.1 ' ]
mock_get_versions . return_value = [ ' 1.0.1 ' , ' 2.0.1-beta.1 ' , ' 2.0.1 ' ]
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_get_versions )
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_get_versions )
actual = collection . CollectionRequirement . from_name ( ' namespace.collection ' , [ galaxy_server ] , ' * ' , True , True )
mock_get_info = MagicMock ( )
mock_get_info . return_value = api . CollectionVersionMetadata ( ' namespace ' , ' collection ' , ' 2.0.1 ' , None , None , { } )
monkeypatch . setattr ( galaxy_server , ' get_collection_version_metadata ' , mock_get_info )
test_dir = to_bytes ( tmp_path_factory . mktemp ( ' test-ÅÑŚÌβŁÈ Collections Input ' ) )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( test_dir , validate_certs = False )
cli = GalaxyCLI ( args = [ ' ansible-galaxy ' , ' collection ' , ' install ' , ' namespace.collection ' ] )
requirements = cli . _require_one_of_collections_requirements (
[ ' namespace.collection ' ] , None , artifacts_manager = concrete_artifact_cm
) [ ' collections ' ]
actual = collection . _resolve_depenency_map ( requirements , [ galaxy_server ] , concrete_artifact_cm , None , True , False ) [ ' namespace.collection ' ]
assert actual . namespace == u ' namespace '
assert actual . namespace == u ' namespace '
assert actual . name == u ' collection '
assert actual . name == u ' collection '
assert actual . b_path is None
assert actual . src == galaxy_server
assert actual . api == galaxy_server
assert actual . ver == u ' 2.0.1 '
assert actual . skip is False
assert actual . versions == set ( [ u ' 1.0.1 ' , u ' 2.0.1 ' ] )
assert actual . latest_version == u ' 2.0.1 '
assert actual . dependencies == { }
assert mock_get_versions . call_count == 1
assert mock_get_versions . call_count == 1
assert mock_get_versions . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' )
assert mock_get_versions . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' )
def test_build_requirment_from_name_with_prerelease_explicit ( galaxy_server , monkeypatch ) :
def test_build_requirment_from_name_with_prerelease_explicit ( galaxy_server , monkeypatch , tmp_path_factory ) :
mock_get_versions = MagicMock ( )
mock_get_versions . return_value = [ ' 1.0.1 ' , ' 2.0.1-beta.1 ' , ' 2.0.1 ' ]
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_get_versions )
mock_get_info = MagicMock ( )
mock_get_info = MagicMock ( )
mock_get_info . return_value = api . CollectionVersionMetadata ( ' namespace ' , ' collection ' , ' 2.0.1-beta.1 ' , None , None ,
mock_get_info . return_value = api . CollectionVersionMetadata ( ' namespace ' , ' collection ' , ' 2.0.1-beta.1 ' , None , None ,
{ } )
{ } )
monkeypatch . setattr ( galaxy_server , ' get_collection_version_metadata ' , mock_get_info )
monkeypatch . setattr ( galaxy_server , ' get_collection_version_metadata ' , mock_get_info )
actual = collection . CollectionRequirement . from_name ( ' namespace.collection ' , [ galaxy_server ] , ' 2.0.1-beta.1 ' , True ,
test_dir = to_bytes ( tmp_path_factory . mktemp ( ' test-ÅÑŚÌβŁÈ Collections Input ' ) )
True )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( test_dir , validate_certs = False )
cli = GalaxyCLI ( args = [ ' ansible-galaxy ' , ' collection ' , ' install ' , ' namespace.collection:2.0.1-beta.1 ' ] )
requirements = cli . _require_one_of_collections_requirements (
[ ' namespace.collection:2.0.1-beta.1 ' ] , None , artifacts_manager = concrete_artifact_cm
) [ ' collections ' ]
actual = collection . _resolve_depenency_map ( requirements , [ galaxy_server ] , concrete_artifact_cm , None , True , False ) [ ' namespace.collection ' ]
assert actual . namespace == u ' namespace '
assert actual . namespace == u ' namespace '
assert actual . name == u ' collection '
assert actual . name == u ' collection '
assert actual . b_path is None
assert actual . src == galaxy_server
assert actual . api == galaxy_server
assert actual . ver == u ' 2.0.1-beta.1 '
assert actual . skip is False
assert actual . versions == set ( [ u ' 2.0.1-beta.1 ' ] )
assert actual . latest_version == u ' 2.0.1-beta.1 '
assert actual . dependencies == { }
assert mock_get_info . call_count == 1
assert mock_get_info . call_count == 1
assert mock_get_info . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' , ' 2.0.1-beta.1 ' )
assert mock_get_info . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' , ' 2.0.1-beta.1 ' )
def test_build_requirement_from_name_second_server ( galaxy_server , monkeypatch ):
def test_build_requirement_from_name_second_server ( galaxy_server , monkeypatch , tmp_path_factory ):
mock_get_versions = MagicMock ( )
mock_get_versions = MagicMock ( )
mock_get_versions . return_value = [ ' 1.0.1 ' , ' 1.0.2 ' , ' 1.0.3 ' ]
mock_get_versions . return_value = [ ' 1.0.1 ' , ' 1.0.2 ' , ' 1.0.3 ' ]
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_get_versions )
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_get_versions )
mock_get_info = MagicMock ( )
mock_get_info . return_value = api . CollectionVersionMetadata ( ' namespace ' , ' collection ' , ' 1.0.3 ' , None , None , { } )
monkeypatch . setattr ( galaxy_server , ' get_collection_version_metadata ' , mock_get_info )
broken_server = copy . copy ( galaxy_server )
broken_server = copy . copy ( galaxy_server )
broken_server . api_server = ' https://broken.com/ '
broken_server . api_server = ' https://broken.com/ '
mock_version_list = MagicMock ( )
mock_version_list = MagicMock ( )
mock_version_list . return_value = [ ]
mock_version_list . return_value = [ ]
monkeypatch . setattr ( broken_server , ' get_collection_versions ' , mock_version_list )
monkeypatch . setattr ( broken_server , ' get_collection_versions ' , mock_version_list )
actual = collection . CollectionRequirement . from_name ( ' namespace.collection ' , [ broken_server , galaxy_server ] ,
test_dir = to_bytes ( tmp_path_factory . mktemp ( ' test-ÅÑŚÌβŁÈ Collections Input ' ) )
' >1.0.1 ' , False , True )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( test_dir , validate_certs = False )
cli = GalaxyCLI ( args = [ ' ansible-galaxy ' , ' collection ' , ' install ' , ' namespace.collection:>1.0.1 ' ] )
requirements = cli . _require_one_of_collections_requirements (
[ ' namespace.collection:>1.0.1 ' ] , None , artifacts_manager = concrete_artifact_cm
) [ ' collections ' ]
actual = collection . _resolve_depenency_map ( requirements , [ broken_server , galaxy_server ] , concrete_artifact_cm , None , True , False ) [ ' namespace.collection ' ]
assert actual . namespace == u ' namespace '
assert actual . namespace == u ' namespace '
assert actual . name == u ' collection '
assert actual . name == u ' collection '
assert actual . b_path is None
assert actual . src == galaxy_server
# assert actual.api == galaxy_server
assert actual . ver == u ' 1.0.3 '
assert actual . skip is False
assert actual . versions == set ( [ u ' 1.0.2 ' , u ' 1.0.3 ' ] )
assert actual . latest_version == u ' 1.0.3 '
assert actual . dependencies == { }
assert mock_version_list . call_count == 1
assert mock_version_list . call_count == 1
assert mock_version_list . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' )
assert mock_version_list . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' )
@ -426,53 +494,91 @@ def test_build_requirement_from_name_second_server(galaxy_server, monkeypatch):
assert mock_get_versions . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' )
assert mock_get_versions . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' )
def test_build_requirement_from_name_missing ( galaxy_server , monkeypatch ):
def test_build_requirement_from_name_missing ( galaxy_server , monkeypatch , tmp_path_factory ):
mock_open = MagicMock ( )
mock_open = MagicMock ( )
mock_open . return_value = [ ]
mock_open . return_value = [ ]
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_open )
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_open )
expected = " Failed to find collection namespace.collection:* "
test_dir = to_bytes ( tmp_path_factory . mktemp ( ' test-ÅÑŚÌβŁÈ Collections Input ' ) )
with pytest . raises ( AnsibleError , match = expected ) :
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( test_dir , validate_certs = False )
collection . CollectionRequirement . from_name ( ' namespace.collection ' , [ galaxy_server , galaxy_server ] , ' * ' , False ,
True )
cli = GalaxyCLI ( args = [ ' ansible-galaxy ' , ' collection ' , ' install ' , ' namespace.collection:>1.0.1 ' ] )
requirements = cli . _require_one_of_collections_requirements (
[ ' namespace.collection ' ] , None , artifacts_manager = concrete_artifact_cm
) [ ' collections ' ]
def test_build_requirement_from_name_401_unauthorized ( galaxy_server , monkeypatch ) :
expected = " Failed to resolve the requested dependencies map. Could not satisfy the following requirements: \n * namespace.collection:* (direct request) "
with pytest . raises ( AnsibleError , match = re . escape ( expected ) ) :
collection . _resolve_depenency_map ( requirements , [ galaxy_server , galaxy_server ] , concrete_artifact_cm , None , False , True )
def test_build_requirement_from_name_401_unauthorized ( galaxy_server , monkeypatch , tmp_path_factory ) :
mock_open = MagicMock ( )
mock_open = MagicMock ( )
mock_open . side_effect = api . GalaxyError ( urllib_error . HTTPError ( ' https://galaxy.server.com ' , 401 , ' msg ' , { } ,
mock_open . side_effect = api . GalaxyError ( urllib_error . HTTPError ( ' https://galaxy.server.com ' , 401 , ' msg ' , { } ,
StringIO ( ) ) , " error " )
StringIO ( ) ) , " error " )
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_open )
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_open )
test_dir = to_bytes ( tmp_path_factory . mktemp ( ' test-ÅÑŚÌβŁÈ Collections Input ' ) )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( test_dir , validate_certs = False )
cli = GalaxyCLI ( args = [ ' ansible-galaxy ' , ' collection ' , ' install ' , ' namespace.collection:>1.0.1 ' ] )
requirements = cli . _require_one_of_collections_requirements (
[ ' namespace.collection ' ] , None , artifacts_manager = concrete_artifact_cm
) [ ' collections ' ]
expected = " error (HTTP Code: 401, Message: msg) "
expected = " error (HTTP Code: 401, Message: msg) "
with pytest . raises ( api . GalaxyError , match = re . escape ( expected ) ) :
with pytest . raises ( api . GalaxyError , match = re . escape ( expected ) ) :
collection . CollectionRequirement . from_name ( ' namespace.collection ' , [ galaxy_server , galaxy_server ] , ' * ' , False )
collection . _resolve_depenency_map( requirements , [ galaxy_server , galaxy_server ] , concrete_artifact_cm , None , False , False )
def test_build_requirement_from_name_single_version ( galaxy_server , monkeypatch ) :
def test_build_requirement_from_name_single_version ( galaxy_server , monkeypatch , tmp_path_factory ) :
test_dir = to_bytes ( tmp_path_factory . mktemp ( ' test-ÅÑŚÌβŁÈ Collections Input ' ) )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( test_dir , validate_certs = False )
multi_api_proxy = collection . galaxy_api_proxy . MultiGalaxyAPIProxy ( [ galaxy_server ] , concrete_artifact_cm )
dep_provider = dependency_resolution . providers . CollectionDependencyProvider ( apis = multi_api_proxy , concrete_artifacts_manager = concrete_artifact_cm )
matches = RequirementCandidates ( )
mock_find_matches = MagicMock ( side_effect = matches . func_wrapper ( dep_provider . find_matches ) , autospec = True )
monkeypatch . setattr ( dependency_resolution . providers . CollectionDependencyProvider , ' find_matches ' , mock_find_matches )
mock_get_versions = MagicMock ( )
mock_get_versions . return_value = [ ' 2.0.0 ' ]
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_get_versions )
mock_get_info = MagicMock ( )
mock_get_info = MagicMock ( )
mock_get_info . return_value = api . CollectionVersionMetadata ( ' namespace ' , ' collection ' , ' 2.0.0 ' , None , None ,
mock_get_info . return_value = api . CollectionVersionMetadata ( ' namespace ' , ' collection ' , ' 2.0.0 ' , None , None ,
{ } )
{ } )
monkeypatch . setattr ( galaxy_server , ' get_collection_version_metadata ' , mock_get_info )
monkeypatch . setattr ( galaxy_server , ' get_collection_version_metadata ' , mock_get_info )
actual = collection . CollectionRequirement . from_name ( ' namespace.collection ' , [ galaxy_server ] , ' 2.0.0 ' , True ,
cli = GalaxyCLI ( args = [ ' ansible-galaxy ' , ' collection ' , ' install ' , ' namespace.collection:==2.0.0 ' ] )
True )
requirements = cli . _require_one_of_collections_requirements (
[ ' namespace.collection:==2.0.0 ' ] , None , artifacts_manager = concrete_artifact_cm
) [ ' collections ' ]
actual = collection . _resolve_depenency_map ( requirements , [ galaxy_server ] , concrete_artifact_cm , None , False , True ) [ ' namespace.collection ' ]
assert actual . namespace == u ' namespace '
assert actual . namespace == u ' namespace '
assert actual . name == u ' collection '
assert actual . name == u ' collection '
assert actual . b_path is None
assert actual . src == galaxy_server
assert actual . api == galaxy_server
assert actual . ver == u ' 2.0.0 '
assert actual . skip is False
assert [ c . ver for c in matches . candidates ] == [ u ' 2.0.0 ' ]
assert actual . versions == set ( [ u ' 2.0.0 ' ] )
assert actual . latest_version == u ' 2.0.0 '
assert actual . dependencies == { }
assert mock_get_info . call_count == 1
assert mock_get_info . call_count == 1
assert mock_get_info . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' , ' 2.0.0 ' )
assert mock_get_info . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' , ' 2.0.0 ' )
def test_build_requirement_from_name_multiple_versions_one_match ( galaxy_server , monkeypatch ) :
def test_build_requirement_from_name_multiple_versions_one_match ( galaxy_server , monkeypatch , tmp_path_factory ) :
test_dir = to_bytes ( tmp_path_factory . mktemp ( ' test-ÅÑŚÌβŁÈ Collections Input ' ) )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( test_dir , validate_certs = False )
multi_api_proxy = collection . galaxy_api_proxy . MultiGalaxyAPIProxy ( [ galaxy_server ] , concrete_artifact_cm )
dep_provider = dependency_resolution . providers . CollectionDependencyProvider ( apis = multi_api_proxy , concrete_artifacts_manager = concrete_artifact_cm )
matches = RequirementCandidates ( )
mock_find_matches = MagicMock ( side_effect = matches . func_wrapper ( dep_provider . find_matches ) , autospec = True )
monkeypatch . setattr ( dependency_resolution . providers . CollectionDependencyProvider , ' find_matches ' , mock_find_matches )
mock_get_versions = MagicMock ( )
mock_get_versions = MagicMock ( )
mock_get_versions . return_value = [ ' 2.0.0 ' , ' 2.0.1 ' , ' 2.0.2 ' ]
mock_get_versions . return_value = [ ' 2.0.0 ' , ' 2.0.1 ' , ' 2.0.2 ' ]
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_get_versions )
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_get_versions )
@ -482,17 +588,18 @@ def test_build_requirement_from_name_multiple_versions_one_match(galaxy_server,
{ } )
{ } )
monkeypatch . setattr ( galaxy_server , ' get_collection_version_metadata ' , mock_get_info )
monkeypatch . setattr ( galaxy_server , ' get_collection_version_metadata ' , mock_get_info )
actual = collection . CollectionRequirement . from_name ( ' namespace.collection ' , [ galaxy_server ] , ' >=2.0.1,<2.0.2 ' ,
cli = GalaxyCLI ( args = [ ' ansible-galaxy ' , ' collection ' , ' install ' , ' namespace.collection:>=2.0.1,<2.0.2 ' ] )
True , True )
requirements = cli . _require_one_of_collections_requirements (
[ ' namespace.collection:>=2.0.1,<2.0.2 ' ] , None , artifacts_manager = concrete_artifact_cm
) [ ' collections ' ]
actual = collection . _resolve_depenency_map ( requirements , [ galaxy_server ] , concrete_artifact_cm , None , False , True ) [ ' namespace.collection ' ]
assert actual . namespace == u ' namespace '
assert actual . namespace == u ' namespace '
assert actual . name == u ' collection '
assert actual . name == u ' collection '
assert actual . b_path is None
assert actual . src == galaxy_server
assert actual . api == galaxy_server
assert actual . ver == u ' 2.0.1 '
assert actual . skip is False
assert [ c . ver for c in matches . candidates ] == [ u ' 2.0.1 ' ]
assert actual . versions == set ( [ u ' 2.0.1 ' ] )
assert actual . latest_version == u ' 2.0.1 '
assert actual . dependencies == { }
assert mock_get_versions . call_count == 1
assert mock_get_versions . call_count == 1
assert mock_get_versions . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' )
assert mock_get_versions . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' )
@ -501,122 +608,118 @@ def test_build_requirement_from_name_multiple_versions_one_match(galaxy_server,
assert mock_get_info . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' , ' 2.0.1 ' )
assert mock_get_info . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' , ' 2.0.1 ' )
def test_build_requirement_from_name_multiple_version_results ( galaxy_server , monkeypatch ) :
def test_build_requirement_from_name_multiple_version_results ( galaxy_server , monkeypatch , tmp_path_factory ) :
test_dir = to_bytes ( tmp_path_factory . mktemp ( ' test-ÅÑŚÌβŁÈ Collections Input ' ) )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( test_dir , validate_certs = False )
multi_api_proxy = collection . galaxy_api_proxy . MultiGalaxyAPIProxy ( [ galaxy_server ] , concrete_artifact_cm )
dep_provider = dependency_resolution . providers . CollectionDependencyProvider ( apis = multi_api_proxy , concrete_artifacts_manager = concrete_artifact_cm )
matches = RequirementCandidates ( )
mock_find_matches = MagicMock ( side_effect = matches . func_wrapper ( dep_provider . find_matches ) , autospec = True )
monkeypatch . setattr ( dependency_resolution . providers . CollectionDependencyProvider , ' find_matches ' , mock_find_matches )
mock_get_info = MagicMock ( )
mock_get_info . return_value = api . CollectionVersionMetadata ( ' namespace ' , ' collection ' , ' 2.0.5 ' , None , None , { } )
monkeypatch . setattr ( galaxy_server , ' get_collection_version_metadata ' , mock_get_info )
mock_get_versions = MagicMock ( )
mock_get_versions = MagicMock ( )
mock_get_versions . return_value = [ ' 1.0.1 ' , ' 1.0.2 ' , ' 1.0.3 ' ]
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_get_versions )
mock_get_versions . return_value = [ ' 2.0.0 ' , ' 2.0.1 ' , ' 2.0.2 ' , ' 2.0.3 ' , ' 2.0.4 ' , ' 2.0.5 ' ]
mock_get_versions . return_value = [ ' 2.0.0 ' , ' 2.0.1 ' , ' 2.0.2 ' , ' 2.0.3 ' , ' 2.0.4 ' , ' 2.0.5 ' ]
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_get_versions )
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_get_versions )
actual = collection . CollectionRequirement . from_name ( ' namespace.collection ' , [ galaxy_server ] , ' !=2.0.2 ' ,
cli = GalaxyCLI ( args = [ ' ansible-galaxy ' , ' collection ' , ' install ' , ' namespace.collection:!=2.0.2 ' ] )
True , True )
requirements = cli . _require_one_of_collections_requirements (
[ ' namespace.collection:!=2.0.2 ' ] , None , artifacts_manager = concrete_artifact_cm
) [ ' collections ' ]
actual = collection . _resolve_depenency_map ( requirements , [ galaxy_server ] , concrete_artifact_cm , None , False , True ) [ ' namespace.collection ' ]
assert actual . namespace == u ' namespace '
assert actual . namespace == u ' namespace '
assert actual . name == u ' collection '
assert actual . name == u ' collection '
assert actual . b_path is None
assert actual . src == galaxy_server
assert actual . api == galaxy_server
assert actual . ver == u ' 2.0.5 '
assert actual . skip is False
# should be ordered latest to earliest
assert actual . versions == set ( [ u ' 2.0.0 ' , u ' 2.0.1 ' , u ' 2.0.3 ' , u ' 2.0.4 ' , u ' 2.0.5 ' ] )
assert [ c . ver for c in matches . candidates ] == [ u ' 2.0.5 ' , u ' 2.0.4 ' , u ' 2.0.3 ' , u ' 2.0.1 ' , u ' 2.0.0 ' ]
assert actual . latest_version == u ' 2.0.5 '
assert actual . dependencies == { }
assert mock_get_versions . call_count == 1
assert mock_get_versions . call_count == 1
assert mock_get_versions . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' )
assert mock_get_versions . mock_calls [ 0 ] [ 1 ] == ( ' namespace ' , ' collection ' )
@pytest.mark.parametrize ( ' versions, requirement, expected_filter, expected_latest ' , [
def test_candidate_with_conflict ( monkeypatch , tmp_path_factory , galaxy_server ) :
[ [ ' 1.0.0 ' , ' 1.0.1 ' ] , ' * ' , [ ' 1.0.0 ' , ' 1.0.1 ' ] , ' 1.0.1 ' ] ,
[ [ ' 1.0.0 ' , ' 1.0.5 ' , ' 1.1.0 ' ] , ' >1.0.0,<1.1.0 ' , [ ' 1.0.5 ' ] , ' 1.0.5 ' ] ,
[ [ ' 1.0.0 ' , ' 1.0.5 ' , ' 1.1.0 ' ] , ' >1.0.0,<=1.0.5 ' , [ ' 1.0.5 ' ] , ' 1.0.5 ' ] ,
[ [ ' 1.0.0 ' , ' 1.0.5 ' , ' 1.1.0 ' ] , ' >=1.1.0 ' , [ ' 1.1.0 ' ] , ' 1.1.0 ' ] ,
[ [ ' 1.0.0 ' , ' 1.0.5 ' , ' 1.1.0 ' ] , ' !=1.1.0 ' , [ ' 1.0.0 ' , ' 1.0.5 ' ] , ' 1.0.5 ' ] ,
[ [ ' 1.0.0 ' , ' 1.0.5 ' , ' 1.1.0 ' ] , ' ==1.0.5 ' , [ ' 1.0.5 ' ] , ' 1.0.5 ' ] ,
[ [ ' 1.0.0 ' , ' 1.0.5 ' , ' 1.1.0 ' ] , ' 1.0.5 ' , [ ' 1.0.5 ' ] , ' 1.0.5 ' ] ,
[ [ ' 1.0.0 ' , ' 2.0.0 ' , ' 3.0.0 ' ] , ' >=2 ' , [ ' 2.0.0 ' , ' 3.0.0 ' ] , ' 3.0.0 ' ] ,
] )
def test_add_collection_requirements ( versions , requirement , expected_filter , expected_latest ) :
req = collection . CollectionRequirement ( ' namespace ' , ' name ' , None , ' https://galaxy.com ' , versions , requirement ,
False )
assert req . versions == set ( expected_filter )
assert req . latest_version == expected_latest
def test_add_collection_requirement_to_unknown_installed_version ( monkeypatch ) :
mock_display = MagicMock ( )
monkeypatch . setattr ( Display , ' display ' , mock_display )
req = collection . CollectionRequirement ( ' namespace ' , ' name ' , None , ' https://galaxy.com ' , [ ' * ' ] , ' * ' , False ,
skip = True )
req. add_requirement ( ' parent.collection ' , ' 1.0.0 ' )
test_dir = to_bytes ( tmp_path_factory . mktemp ( ' test-ÅÑŚÌβŁÈ Collections Input ' ) )
assert req . latest_version == ' * '
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( test_dir , validate_certs = False )
assert mock_display . call_count == 1
mock_get_info = MagicMock ( )
mock_get_info . return_value = api . CollectionVersionMetadata ( ' namespace ' , ' collection ' , ' 2.0.5 ' , None , None , { } )
monkeypatch . setattr ( galaxy_server , ' get_collection_version_metadata ' , mock_get_info )
actual_warn = ' ' . join ( mock_display . mock_calls [ 0 ] [ 1 ] [ 0 ] . split ( ' \n ' ) )
mock_get_versions = MagicMock ( )
assert " Failed to validate the collection requirement ' namespace.name:1.0.0 ' for parent.collection " in actual_warn
mock_get_versions . return_value = [ ' 2.0.5 ' ]
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_get_versions )
cli = GalaxyCLI ( args = [ ' ansible-galaxy ' , ' collection ' , ' install ' , ' namespace.collection:!=2.0.5 ' ] )
requirements = cli . _require_one_of_collections_requirements (
[ ' namespace.collection:!=2.0.5 ' ] , None , artifacts_manager = concrete_artifact_cm
) [ ' collections ' ]
def test_add_collection_wildcard_requirement_to_unknown_installed_version ( ) :
expected = " Failed to resolve the requested dependencies map. Could not satisfy the following requirements: \n "
req = collection . CollectionRequirement ( ' namespace ' , ' name ' , None , ' https://galaxy.com ' , [ ' * ' ] , ' * ' , False ,
expected + = " * namespace.collection:!=2.0.5 (direct request) "
skip = True )
with pytest . raises ( AnsibleError , match = re . escape ( expected ) ) :
req . add_requirement ( str ( req ) , ' * ' )
collection . _resolve_depenency_map ( requirements , [ galaxy_server ] , concrete_artifact_cm , None , False , True )
assert req . versions == set ( ' * ' )
assert req . latest_version == ' * '
def test_dep_candidate_with_conflict ( monkeypatch , tmp_path_factory , galaxy_server ) :
test_dir = to_bytes ( tmp_path_factory . mktemp ( ' test-ÅÑŚÌβŁÈ Collections Input ' ) )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( test_dir , validate_certs = False )
def test_add_collection_requirement_with_conflict ( galaxy_server ) :
mock_get_info_return = [
expected = " Cannot meet requirement ==1.0.2 for dependency namespace.name from source ' %s ' . Available versions " \
api . CollectionVersionMetadata ( ' parent ' , ' collection ' , ' 2.0.5 ' , None , None , { ' namespace.collection ' : ' !=1.0.0 ' } ) ,
" before last requirement added: 1.0.0, 1.0.1 \n " \
api . CollectionVersionMetadata ( ' namespace ' , ' collection ' , ' 1.0.0 ' , None , None , { } ) ,
" Requirements from: \n " \
]
" \t base - ' namespace.name:==1.0.2 ' " % galaxy_server . api_server
mock_get_info = MagicMock ( side_effect = mock_get_info_return )
with pytest . raises ( AnsibleError , match = expected ) :
monkeypatch . setattr ( galaxy_server , ' get_collection_version_metadata ' , mock_get_info )
collection . CollectionRequirement ( ' namespace ' , ' name ' , None , galaxy_server , [ ' 1.0.0 ' , ' 1.0.1 ' ] , ' ==1.0.2 ' ,
False )
mock_get_versions = MagicMock ( side_effect = [ [ ' 2.0.5 ' ] , [ ' 1.0.0 ' ] ] )
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_get_versions )
def test_add_requirement_to_existing_collection_with_conflict ( galaxy_server ) :
cli = GalaxyCLI ( args = [ ' ansible-galaxy ' , ' collection ' , ' install ' , ' parent.collection:2.0.5 ' ] )
req = collection . CollectionRequirement ( ' namespace ' , ' name ' , None , galaxy_server , [ ' 1.0.0 ' , ' 1.0.1 ' ] , ' * ' , False )
requirements = cli . _require_one_of_collections_requirements (
[ ' parent.collection:2.0.5 ' ] , None , artifacts_manager = concrete_artifact_cm
) [ ' collections ' ]
expected = " Cannot meet dependency requirement ' namespace.name:1.0.2 ' for collection namespace.collection2 from " \
expected = " Failed to resolve the requested dependencies map. Could not satisfy the following requirements: \n "
" source ' %s ' . Available versions before last requirement added: 1.0.0, 1.0.1 \n " \
expected + = " * namespace.collection:!=1.0.0 (dependency of parent.collection:2.0.5) "
" Requirements from: \n " \
" \t base - ' namespace.name:* ' \n " \
" \t namespace.collection2 - ' namespace.name:1.0.2 ' " % galaxy_server . api_server
with pytest . raises ( AnsibleError , match = re . escape ( expected ) ) :
with pytest . raises ( AnsibleError , match = re . escape ( expected ) ) :
req. add_requirement ( ' namespace.collection2 ' , ' 1.0.2 ' )
collection . _resolve_depenency_map ( requirements , [ galaxy_server ] , concrete_artifact_cm , None , False , True )
def test_add_requirement_to_installed_collection_with_conflict ( ) :
def test_install_installed_collection ( monkeypatch , tmp_path_factory , galaxy_server ) :
source = ' https://galaxy.ansible.com '
req = collection . CollectionRequirement ( ' namespace ' , ' name ' , None , source , [ ' 1.0.0 ' , ' 1.0.1 ' ] , ' * ' , False ,
skip = True )
expected = " Cannot meet requirement namespace.name:1.0.2 as it is already installed at version ' 1.0.1 ' . " \
mock_installed_collections = MagicMock ( return_value = [ Candidate ( ' namespace.collection ' , ' 1.2.3 ' , None , ' dir ' ) ] )
" Use --force to overwrite "
with pytest . raises ( AnsibleError , match = re . escape ( expected ) ) :
req . add_requirement ( None , ' 1.0.2 ' )
monkeypatch . setattr ( collection , ' find_existing_collections ' , mock_installed_collections )
def test_add_requirement_to_installed_collection_with_conflict_as_dep ( ) :
test_dir = to_text ( tmp_path_factory . mktemp ( ' test-ÅÑŚÌβŁÈ Collections ' ) )
source = ' https://galaxy.ansible.com '
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( test_dir , validate_certs = False )
req = collection . CollectionRequirement ( ' namespace ' , ' name ' , None , source , [ ' 1.0.0 ' , ' 1.0.1 ' ] , ' * ' , False ,
skip = True )
expected = " Cannot meet requirement namespace.name:1.0.2 as it is already installed at version ' 1.0.1 ' . " \
" Use --force-with-deps to overwrite "
with pytest . raises ( AnsibleError , match = re . escape ( expected ) ) :
req . add_requirement ( ' namespace.collection2 ' , ' 1.0.2 ' )
def test_install_skipped_collection ( monkeypatch ) :
mock_display = MagicMock ( )
mock_display = MagicMock ( )
monkeypatch . setattr ( Display , ' display ' , mock_display )
monkeypatch . setattr ( Display , ' display ' , mock_display )
req = collection . CollectionRequirement ( ' namespace ' , ' name ' , None , ' source ' , [ ' 1.0.0 ' ] , ' * ' , False , skip = True )
mock_get_info = MagicMock ( )
req . install ( None , None )
mock_get_info . return_value = api . CollectionVersionMetadata ( ' namespace ' , ' collection ' , ' 1.2.3 ' , None , None , { } )
monkeypatch . setattr ( galaxy_server , ' get_collection_version_metadata ' , mock_get_info )
mock_get_versions = MagicMock ( return_value = [ ' 1.2.3 ' , ' 1.3.0 ' ] )
monkeypatch . setattr ( galaxy_server , ' get_collection_versions ' , mock_get_versions )
cli = GalaxyCLI ( args = [ ' ansible-galaxy ' , ' collection ' , ' install ' , ' namespace.collection ' ] )
cli . run ( )
assert mock_display . call_count == 1
expected = " Nothing to do. All requested collections are already installed. If you want to reinstall them, consider using `--force`. "
assert mock_display . mock_calls [ 0 ] [ 1 ] [ 0 ] == " Skipping ' namespace.name ' as it is already installed "
assert mock_display . mock_calls [ 1] [ 1 ] [ 0 ] == expected
def test_install_collection ( collection_artifact , monkeypatch ) :
def test_install_collection ( collection_artifact , monkeypatch ) :
@ -624,15 +727,17 @@ def test_install_collection(collection_artifact, monkeypatch):
monkeypatch . setattr ( Display , ' display ' , mock_display )
monkeypatch . setattr ( Display , ' display ' , mock_display )
collection_tar = collection_artifact [ 1 ]
collection_tar = collection_artifact [ 1 ]
output_path = os . path . join ( os . path . split ( collection_tar ) [ 0 ] , b ' output ' )
collection_path = os . path . join ( output_path , b ' ansible_namespace ' , b ' collection ' )
os . makedirs ( os . path . join ( collection_path , b ' delete_me ' ) ) # Create a folder to verify the install cleans out the dir
temp_path = os . path . join ( os . path . split ( collection_tar ) [ 0 ] , b ' temp ' )
temp_path = os . path . join ( os . path . split ( collection_tar ) [ 0 ] , b ' temp ' )
os . makedirs ( temp_path )
os . makedirs ( temp_path )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( temp_path , validate_certs = False )
req = collection . CollectionRequirement . from_tar ( collection_tar , True , True )
output_path = os . path . join ( os . path . split ( collection_tar ) [ 0 ] )
req . install ( to_text ( output_path ) , temp_path )
collection_path = os . path . join ( output_path , b ' ansible_namespace ' , b ' collection ' )
os . makedirs ( os . path . join ( collection_path , b ' delete_me ' ) ) # Create a folder to verify the install cleans out the dir
candidate = Candidate ( ' ansible_namespace.collection ' , ' 0.1.0 ' , to_text ( collection_tar ) , ' file ' )
collection . install ( candidate , to_text ( output_path ) , concrete_artifact_cm )
# Ensure the temp directory is empty, nothing is left behind
# Ensure the temp directory is empty, nothing is left behind
assert os . listdir ( temp_path ) == [ ]
assert os . listdir ( temp_path ) == [ ]
@ -649,33 +754,29 @@ def test_install_collection(collection_artifact, monkeypatch):
assert mock_display . call_count == 2
assert mock_display . call_count == 2
assert mock_display . mock_calls [ 0 ] [ 1 ] [ 0 ] == " Installing ' ansible_namespace.collection:0.1.0 ' to ' %s ' " \
assert mock_display . mock_calls [ 0 ] [ 1 ] [ 0 ] == " Installing ' ansible_namespace.collection:0.1.0 ' to ' %s ' " \
% to_text ( collection_path )
% to_text ( collection_path )
assert mock_display . mock_calls [ 1 ] [ 1 ] [ 0 ] == " ansible_namespace.collection (0.1.0) was installed successfully"
assert mock_display . mock_calls [ 1 ] [ 1 ] [ 0 ] == " ansible_namespace.collection :0.1.0 was installed successfully"
def test_install_collection_with_download ( galaxy_server , collection_artifact , monkeypatch ) :
def test_install_collection_with_download ( galaxy_server , collection_artifact , monkeypatch ) :
collection_tar = collection_artifact [ 1 ]
collection_path , collection_tar = collection_artifact
output_path = os . path . join ( os . path . split ( collection_tar ) [ 0 ] , b ' output ' )
shutil . rmtree ( collection_path )
collection_path = os . path . join ( output_path , b ' ansible_namespace ' , b ' collection ' )
collections_dir = ( ' %s ' % os . path . sep ) . join ( to_text ( collection_path ) . split ( ' %s ' % os . path . sep ) [ : - 2 ] )
temp_path = os . path . join ( os . path . split ( collection_tar ) [ 0 ] , b ' temp ' )
os . makedirs ( temp_path )
mock_display = MagicMock ( )
mock_display = MagicMock ( )
monkeypatch . setattr ( Display , ' display ' , mock_display )
monkeypatch . setattr ( Display , ' display ' , mock_display )
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( temp_path , validate_certs = False )
mock_download = MagicMock ( )
mock_download = MagicMock ( )
mock_download . return_value = collection_tar
mock_download . return_value = collection_tar
monkeypatch . setattr ( collection , ' _download_file ' , mock_download )
monkeypatch . setattr ( concrete_artifact_cm , ' get_galaxy_artifact_path ' , mock_download )
monkeypatch . setattr ( galaxy_server , ' _available_api_versions ' , { ' v2 ' : ' v2/ ' } )
temp_path = os . path . join ( os . path . split ( collection_tar ) [ 0 ] , b ' temp ' )
os . makedirs ( temp_path )
meta = api . CollectionVersionMetadata ( ' ansible_namespace ' , ' collection ' , ' 0.1.0 ' , ' https://downloadme.com ' ,
req = Requirement ( ' ansible_namespace.collection ' , ' 0.1.0 ' , ' https://downloadme.com ' , ' galaxy ' )
' myhash ' , { } )
collection . install ( req , to_text ( collections_dir ) , concrete_artifact_cm )
req = collection . CollectionRequirement ( ' ansible_namespace ' , ' collection ' , None , galaxy_server ,
[ ' 0.1.0 ' ] , ' * ' , False , metadata = meta )
req . install ( to_text ( output_path ) , temp_path )
# Ensure the temp directory is empty, nothing is left behind
assert os . listdir ( temp_path ) == [ ]
actual_files = os . listdir ( collection_path )
actual_files = os . listdir ( collection_path )
actual_files . sort ( )
actual_files . sort ( )
@ -685,13 +786,11 @@ def test_install_collection_with_download(galaxy_server, collection_artifact, mo
assert mock_display . call_count == 2
assert mock_display . call_count == 2
assert mock_display . mock_calls [ 0 ] [ 1 ] [ 0 ] == " Installing ' ansible_namespace.collection:0.1.0 ' to ' %s ' " \
assert mock_display . mock_calls [ 0 ] [ 1 ] [ 0 ] == " Installing ' ansible_namespace.collection:0.1.0 ' to ' %s ' " \
% to_text ( collection_path )
% to_text ( collection_path )
assert mock_display . mock_calls [ 1 ] [ 1 ] [ 0 ] == " ansible_namespace.collection (0.1.0) was installed successfully"
assert mock_display . mock_calls [ 1 ] [ 1 ] [ 0 ] == " ansible_namespace.collection :0.1.0 was installed successfully"
assert mock_download . call_count == 1
assert mock_download . call_count == 1
assert mock_download . mock_calls [ 0 ] [ 1 ] [ 0 ] == ' https://downloadme.com '
assert mock_download . mock_calls [ 0 ] [ 1 ] [ 0 ] . src == ' https://downloadme.com '
assert mock_download . mock_calls [ 0 ] [ 1 ] [ 1 ] == temp_path
assert mock_download . mock_calls [ 0 ] [ 1 ] [ 0 ] . type == ' galaxy '
assert mock_download . mock_calls [ 0 ] [ 1 ] [ 2 ] == ' myhash '
assert mock_download . mock_calls [ 0 ] [ 1 ] [ 3 ] is True
def test_install_collections_from_tar ( collection_artifact , monkeypatch ) :
def test_install_collections_from_tar ( collection_artifact , monkeypatch ) :
@ -702,8 +801,10 @@ def test_install_collections_from_tar(collection_artifact, monkeypatch):
mock_display = MagicMock ( )
mock_display = MagicMock ( )
monkeypatch . setattr ( Display , ' display ' , mock_display )
monkeypatch . setattr ( Display , ' display ' , mock_display )
collection . install_collections ( [ ( to_text ( collection_tar ) , ' * ' , None , None ) ] , to_text ( temp_path ) ,
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( temp_path , validate_certs = False )
[ u ' https://galaxy.ansible.com ' ] , True , False , False , False , False )
requirements = [ Requirement ( ' ansible_namespace.collection ' , ' 0.1.0 ' , to_text ( collection_tar ) , ' file ' ) ]
collection . install_collections ( requirements , to_text ( temp_path ) , [ ] , False , False , False , False , False , concrete_artifact_cm )
assert os . path . isdir ( collection_path )
assert os . path . isdir ( collection_path )
@ -734,9 +835,12 @@ def test_install_collections_existing_without_force(collection_artifact, monkeyp
mock_display = MagicMock ( )
mock_display = MagicMock ( )
monkeypatch . setattr ( Display , ' display ' , mock_display )
monkeypatch . setattr ( Display , ' display ' , mock_display )
# If we don't delete collection_path it will think the original build skeleton is installed so we expect a skip
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( temp_path , validate_certs = False )
collection . install_collections ( [ ( to_text ( collection_tar ) , ' * ' , None , None ) ] , to_text ( temp_path ) ,
[ u ' https://galaxy.ansible.com ' ] , True , False , False , False , False )
assert os . path . isdir ( collection_path )
requirements = [ Requirement ( ' ansible_namespace.collection ' , ' 0.1.0 ' , to_text ( collection_tar ) , ' file ' ) ]
collection . install_collections ( requirements , to_text ( temp_path ) , [ ] , False , False , False , False , False , concrete_artifact_cm )
assert os . path . isdir ( collection_path )
assert os . path . isdir ( collection_path )
@ -746,11 +850,9 @@ def test_install_collections_existing_without_force(collection_artifact, monkeyp
# Filter out the progress cursor display calls.
# Filter out the progress cursor display calls.
display_msgs = [ m [ 1 ] [ 0 ] for m in mock_display . mock_calls if ' newline ' not in m [ 2 ] and len ( m [ 1 ] ) == 1 ]
display_msgs = [ m [ 1 ] [ 0 ] for m in mock_display . mock_calls if ' newline ' not in m [ 2 ] and len ( m [ 1 ] ) == 1 ]
assert len ( display_msgs ) == 3
assert len ( display_msgs ) == 1
assert display_msgs [ 0 ] == " Process install dependency map "
assert display_msgs [ 0 ] == ' Nothing to do. All requested collections are already installed. If you want to reinstall them, consider using `--force`. '
assert display_msgs [ 1 ] == " Starting collection install process "
assert display_msgs [ 2 ] == " Skipping ' ansible_namespace.collection ' as it is already installed "
for msg in display_msgs :
for msg in display_msgs :
assert ' WARNING ' not in msg
assert ' WARNING ' not in msg
@ -768,8 +870,9 @@ def test_install_missing_metadata_warning(collection_artifact, monkeypatch):
if os . path . isfile ( b_path ) :
if os . path . isfile ( b_path ) :
os . unlink ( b_path )
os . unlink ( b_path )
collection . install_collections ( [ ( to_text ( collection_tar ) , ' * ' , None , None ) ] , to_text ( temp_path ) ,
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( temp_path , validate_certs = False )
[ u ' https://galaxy.ansible.com ' ] , True , False , False , False , False )
requirements = [ Requirement ( ' ansible_namespace.collection ' , ' 0.1.0 ' , to_text ( collection_tar ) , ' file ' ) ]
collection . install_collections ( requirements , to_text ( temp_path ) , [ ] , False , False , False , False , False , concrete_artifact_cm )
display_msgs = [ m [ 1 ] [ 0 ] for m in mock_display . mock_calls if ' newline ' not in m [ 2 ] and len ( m [ 1 ] ) == 1 ]
display_msgs = [ m [ 1 ] [ 0 ] for m in mock_display . mock_calls if ' newline ' not in m [ 2 ] and len ( m [ 1 ] ) == 1 ]
@ -788,8 +891,9 @@ def test_install_collection_with_circular_dependency(collection_artifact, monkey
mock_display = MagicMock ( )
mock_display = MagicMock ( )
monkeypatch . setattr ( Display , ' display ' , mock_display )
monkeypatch . setattr ( Display , ' display ' , mock_display )
collection . install_collections ( [ ( to_text ( collection_tar ) , ' * ' , None , None ) ] , to_text ( temp_path ) ,
concrete_artifact_cm = collection . concrete_artifact_manager . ConcreteArtifactsManager ( temp_path , validate_certs = False )
[ u ' https://galaxy.ansible.com ' ] , True , False , False , False , False )
requirements = [ Requirement ( ' ansible_namespace.collection ' , ' 0.1.0 ' , to_text ( collection_tar ) , ' file ' ) ]
collection . install_collections ( requirements , to_text ( temp_path ) , [ ] , False , False , False , False , False , concrete_artifact_cm )
assert os . path . isdir ( collection_path )
assert os . path . isdir ( collection_path )
@ -811,4 +915,4 @@ def test_install_collection_with_circular_dependency(collection_artifact, monkey
assert display_msgs [ 0 ] == " Process install dependency map "
assert display_msgs [ 0 ] == " Process install dependency map "
assert display_msgs [ 1 ] == " Starting collection install process "
assert display_msgs [ 1 ] == " Starting collection install process "
assert display_msgs [ 2 ] == " Installing ' ansible_namespace.collection:0.1.0 ' to ' %s ' " % to_text ( collection_path )
assert display_msgs [ 2 ] == " Installing ' ansible_namespace.collection:0.1.0 ' to ' %s ' " % to_text ( collection_path )
assert display_msgs [ 3 ] == " ansible_namespace.collection (0.1.0) was installed successfully"
assert display_msgs [ 3 ] == " ansible_namespace.collection :0.1.0 was installed successfully"