@ -34,6 +34,7 @@ from collections import OrderedDict
from contextlib import contextmanager
from contextlib import contextmanager
from distutils . version import StrictVersion
from distutils . version import StrictVersion
from fnmatch import fnmatch
from fnmatch import fnmatch
import yaml
from ansible import __version__ as ansible_version
from ansible import __version__ as ansible_version
from ansible . executor . module_common import REPLACER_WINDOWS
from ansible . executor . module_common import REPLACER_WINDOWS
@ -237,7 +238,7 @@ class ModuleValidator(Validator):
WHITELIST_FUTURE_IMPORTS = frozenset ( ( ' absolute_import ' , ' division ' , ' print_function ' ) )
WHITELIST_FUTURE_IMPORTS = frozenset ( ( ' absolute_import ' , ' division ' , ' print_function ' ) )
def __init__ ( self , path , analyze_arg_spec = False , collection = None , base_branch = None , git_cache = None , reporter = None ):
def __init__ ( self , path , analyze_arg_spec = False , collection = None , base_branch = None , git_cache = None , reporter = None , routing = None ):
super ( ModuleValidator , self ) . __init__ ( reporter = reporter or Reporter ( ) )
super ( ModuleValidator , self ) . __init__ ( reporter = reporter or Reporter ( ) )
self . path = path
self . path = path
@ -247,6 +248,7 @@ class ModuleValidator(Validator):
self . analyze_arg_spec = analyze_arg_spec
self . analyze_arg_spec = analyze_arg_spec
self . collection = collection
self . collection = collection
self . routing = routing
self . base_branch = base_branch
self . base_branch = base_branch
self . git_cache = git_cache or GitCache ( )
self . git_cache = git_cache or GitCache ( )
@ -882,6 +884,7 @@ class ModuleValidator(Validator):
deprecated = False
deprecated = False
removed = False
removed = False
doc_deprecated = None # doc legally might not exist
doc_deprecated = None # doc legally might not exist
routing_says_deprecated = False
if self . object_name . startswith ( ' _ ' ) and not os . path . islink ( self . object_path ) :
if self . object_name . startswith ( ' _ ' ) and not os . path . islink ( self . object_path ) :
filename_deprecated_or_removed = True
filename_deprecated_or_removed = True
@ -923,6 +926,12 @@ class ModuleValidator(Validator):
code = ' missing-metadata-status ' ,
code = ' missing-metadata-status ' ,
msg = ' ANSIBLE_METADATA.status must be exactly one of " deprecated " or " removed " '
msg = ' ANSIBLE_METADATA.status must be exactly one of " deprecated " or " removed " '
)
)
else :
# We are testing a collection
if self . routing and self . routing . get ( ' plugin_routing ' , { } ) . get ( ' modules ' , { } ) . get ( self . name , { } ) . get ( ' deprecation ' , { } ) :
# meta/routing.yml says this is deprecated
routing_says_deprecated = True
deprecated = True
if not removed :
if not removed :
if not bool ( doc_info [ ' DOCUMENTATION ' ] [ ' value ' ] ) :
if not bool ( doc_info [ ' DOCUMENTATION ' ] [ ' value ' ] ) :
@ -995,7 +1004,8 @@ class ModuleValidator(Validator):
doc ,
doc ,
doc_schema (
doc_schema (
os . readlink ( self . object_path ) . split ( ' . ' ) [ 0 ] ,
os . readlink ( self . object_path ) . split ( ' . ' ) [ 0 ] ,
version_added = not bool ( self . collection )
version_added = not bool ( self . collection ) ,
deprecated_module = deprecated ,
) ,
) ,
' DOCUMENTATION ' ,
' DOCUMENTATION ' ,
' invalid-documentation ' ,
' invalid-documentation ' ,
@ -1006,7 +1016,8 @@ class ModuleValidator(Validator):
doc ,
doc ,
doc_schema (
doc_schema (
self . object_name . split ( ' . ' ) [ 0 ] ,
self . object_name . split ( ' . ' ) [ 0 ] ,
version_added = not bool ( self . collection )
version_added = not bool ( self . collection ) ,
deprecated_module = deprecated ,
) ,
) ,
' DOCUMENTATION ' ,
' DOCUMENTATION ' ,
' invalid-documentation ' ,
' invalid-documentation ' ,
@ -1070,6 +1081,7 @@ class ModuleValidator(Validator):
)
)
# Check for mismatched deprecation
# Check for mismatched deprecation
if not self . collection :
mismatched_deprecation = True
mismatched_deprecation = True
if not ( filename_deprecated_or_removed or removed or deprecated or doc_deprecated ) :
if not ( filename_deprecated_or_removed or removed or deprecated or doc_deprecated ) :
mismatched_deprecation = False
mismatched_deprecation = False
@ -1087,6 +1099,24 @@ class ModuleValidator(Validator):
' " _ " , and setting DOCUMENTATION.deprecated for deprecation or by removing all '
' " _ " , and setting DOCUMENTATION.deprecated for deprecation or by removing all '
' documentation for removed '
' documentation for removed '
)
)
else :
# We are testing a collection
if self . object_name . startswith ( ' _ ' ) :
self . reporter . error (
path = self . object_path ,
code = ' collections-no-underscore-on-deprecation ' ,
msg = ' Deprecated content in collections MUST NOT start with " _ " , update meta/routing.yml instead ' ,
)
if not ( doc_deprecated == routing_says_deprecated ) :
# DOCUMENTATION.deprecated and meta/routing.yml disagree
self . reporter . error (
path = self . object_path ,
code = ' deprecation-mismatch ' ,
msg = ' " meta/routing.yml " and DOCUMENTATION.deprecation do not agree. '
)
# In the future we should error if ANSIBLE_METADATA exists in a collection
return doc_info , doc
return doc_info , doc
@ -2111,8 +2141,19 @@ def run():
check_dirs = set ( )
check_dirs = set ( )
routing = None
if args . collection :
if args . collection :
setup_collection_loader ( )
setup_collection_loader ( )
routing_file = ' meta/routing.yml '
# Load meta/routing.yml if it exists, as it may contain deprecation information
if os . path . isfile ( routing_file ) :
try :
with open ( routing_file ) as f :
routing = yaml . safe_load ( f )
except yaml . error . MarkedYAMLError as ex :
print ( ' %s : %d : %d : YAML load failed: %s ' % ( routing_file , ex . context_mark . line + 1 , ex . context_mark . column + 1 , re . sub ( r ' \ s+ ' , ' ' , str ( ex ) ) ) )
except Exception as ex : # pylint: disable=broad-except
print ( ' %s : %d : %d : YAML load failed: %s ' % ( routing_file , 0 , 0 , re . sub ( r ' \ s+ ' , ' ' , str ( ex ) ) ) )
for module in args . modules :
for module in args . modules :
if os . path . isfile ( module ) :
if os . path . isfile ( module ) :
@ -2122,7 +2163,7 @@ def run():
if ModuleValidator . is_blacklisted ( path ) :
if ModuleValidator . is_blacklisted ( path ) :
continue
continue
with ModuleValidator ( path , collection = args . collection , analyze_arg_spec = args . arg_spec ,
with ModuleValidator ( path , collection = args . collection , analyze_arg_spec = args . arg_spec ,
base_branch = args . base_branch , git_cache = git_cache , reporter = reporter ) as mv1 :
base_branch = args . base_branch , git_cache = git_cache , reporter = reporter , routing = routing ) as mv1 :
mv1 . validate ( )
mv1 . validate ( )
check_dirs . add ( os . path . dirname ( path ) )
check_dirs . add ( os . path . dirname ( path ) )
@ -2145,7 +2186,7 @@ def run():
if ModuleValidator . is_blacklisted ( path ) :
if ModuleValidator . is_blacklisted ( path ) :
continue
continue
with ModuleValidator ( path , collection = args . collection , analyze_arg_spec = args . arg_spec ,
with ModuleValidator ( path , collection = args . collection , analyze_arg_spec = args . arg_spec ,
base_branch = args . base_branch , git_cache = git_cache , reporter = reporter ) as mv2 :
base_branch = args . base_branch , git_cache = git_cache , reporter = reporter , routing = routing ) as mv2 :
mv2 . validate ( )
mv2 . validate ( )
if not args . collection :
if not args . collection :