@ -20,6 +20,7 @@ from __future__ import absolute_import, division, print_function
import copy
import copy
from datetime import datetime
from datetime import datetime
from distutils . version import LooseVersion
import time
import time
import sys
import sys
@ -31,14 +32,28 @@ from ansible.module_utils.common.dict_transformations import dict_merge
try :
try :
import yaml
import yaml
from openshift . dynamic . exceptions import DynamicApiError , NotFoundError , ConflictError , ForbiddenError
from openshift . dynamic . exceptions import DynamicApiError , NotFoundError , ConflictError , ForbiddenError , KubernetesValidateMissing
except ImportError :
except ImportError :
# Exceptions handled in common
# Exceptions handled in common
pass
pass
try :
import kubernetes_validate
HAS_KUBERNETES_VALIDATE = True
except ImportError :
HAS_KUBERNETES_VALIDATE = False
class KubernetesRawModule ( KubernetesAnsibleModule ) :
class KubernetesRawModule ( KubernetesAnsibleModule ) :
@property
def validate_spec ( self ) :
return dict (
fail_on_error = dict ( type = ' bool ' ) ,
version = dict ( ) ,
strict = dict ( type = ' bool ' , default = True )
)
@property
@property
def argspec ( self ) :
def argspec ( self ) :
argument_spec = copy . deepcopy ( COMMON_ARG_SPEC )
argument_spec = copy . deepcopy ( COMMON_ARG_SPEC )
@ -46,6 +61,7 @@ class KubernetesRawModule(KubernetesAnsibleModule):
argument_spec [ ' merge_type ' ] = dict ( type = ' list ' , choices = [ ' json ' , ' merge ' , ' strategic-merge ' ] )
argument_spec [ ' merge_type ' ] = dict ( type = ' list ' , choices = [ ' json ' , ' merge ' , ' strategic-merge ' ] )
argument_spec [ ' wait ' ] = dict ( type = ' bool ' , default = False )
argument_spec [ ' wait ' ] = dict ( type = ' bool ' , default = False )
argument_spec [ ' wait_timeout ' ] = dict ( type = ' int ' , default = 120 )
argument_spec [ ' wait_timeout ' ] = dict ( type = ' int ' , default = 120 )
argument_spec [ ' validate ' ] = dict ( type = ' dict ' , default = None , options = self . validate_spec )
return argument_spec
return argument_spec
def __init__ ( self , * args , * * kwargs ) :
def __init__ ( self , * args , * * kwargs ) :
@ -59,12 +75,17 @@ class KubernetesRawModule(KubernetesAnsibleModule):
mutually_exclusive = mutually_exclusive ,
mutually_exclusive = mutually_exclusive ,
supports_check_mode = True ,
supports_check_mode = True ,
* * kwargs )
* * kwargs )
self . kind = self . params . get ( ' kind ' )
self . kind = self . params . pop ( ' kind ' )
self . api_version = self . params . get ( ' api_version ' )
self . api_version = self . params . pop ( ' api_version ' )
self . name = self . params . get ( ' name ' )
self . name = self . params . pop ( ' name ' )
self . namespace = self . params . get ( ' namespace ' )
self . namespace = self . params . pop ( ' namespace ' )
resource_definition = self . params . get ( ' resource_definition ' )
resource_definition = self . params . pop ( ' resource_definition ' )
if self . params [ ' validate ' ] :
if LooseVersion ( self . openshift_version ) < LooseVersion ( " 0.8.0 " ) :
self . fail_json ( msg = " openshift >= 0.8.0 is required for validate " )
if self . params [ ' merge_type ' ] :
if LooseVersion ( self . openshift_version ) < LooseVersion ( " 0.6.2 " ) :
self . fail_json ( msg = " openshift >= 0.6.2 is required for merge_type " )
if resource_definition :
if resource_definition :
if isinstance ( resource_definition , string_types ) :
if isinstance ( resource_definition , string_types ) :
try :
try :
@ -101,7 +122,11 @@ class KubernetesRawModule(KubernetesAnsibleModule):
api_version = definition . get ( ' apiVersion ' , self . api_version )
api_version = definition . get ( ' apiVersion ' , self . api_version )
resource = self . find_resource ( search_kind , api_version , fail = True )
resource = self . find_resource ( search_kind , api_version , fail = True )
definition = self . set_defaults ( resource , definition )
definition = self . set_defaults ( resource , definition )
self . warnings = [ ]
if self . params [ ' validate ' ] is not None :
self . warnings = self . validate ( definition )
result = self . perform_action ( resource , definition )
result = self . perform_action ( resource , definition )
result [ ' warnings ' ] = self . warnings
changed = changed or result [ ' changed ' ]
changed = changed or result [ ' changed ' ]
results . append ( result )
results . append ( result )
@ -115,6 +140,17 @@ class KubernetesRawModule(KubernetesAnsibleModule):
}
}
} )
} )
def validate ( self , resource ) :
try :
warnings , errors = self . client . validate ( resource , self . params [ ' validate ' ] . get ( ' version ' ) , self . params [ ' validate ' ] . get ( ' strict ' ) )
except KubernetesValidateMissing :
self . fail_json ( msg = " kubernetes-validate python library is required to validate resources " )
if errors and self . params [ ' validate ' ] [ ' fail_on_error ' ] :
self . fail_json ( msg = " \n " . join ( errors ) )
else :
return warnings + errors
def set_defaults ( self , resource , definition ) :
def set_defaults ( self , resource , definition ) :
definition [ ' kind ' ] = resource . kind
definition [ ' kind ' ] = resource . kind
definition [ ' apiVersion ' ] = resource . group_version
definition [ ' apiVersion ' ] = resource . group_version
@ -198,8 +234,10 @@ class KubernetesRawModule(KubernetesAnsibleModule):
if the resource you are creating does not directly create a resource of the same kind . " .format(name))
if the resource you are creating does not directly create a resource of the same kind . " .format(name))
return result
return result
except DynamicApiError as exc :
except DynamicApiError as exc :
self . fail_json ( msg = " Failed to create object: {0} " . format ( exc . body ) ,
msg = " Failed to create object: {0} " . format ( exc . body )
error = exc . status , status = exc . status , reason = exc . reason , definition = definition )
if self . warnings :
msg + = " \n " + " \n " . join ( self . warnings )
self . fail_json ( msg = msg , error = exc . status , status = exc . status , reason = exc . reason )
success = True
success = True
result [ ' result ' ] = k8s_obj
result [ ' result ' ] = k8s_obj
if wait :
if wait :
@ -220,8 +258,11 @@ class KubernetesRawModule(KubernetesAnsibleModule):
try :
try :
k8s_obj = resource . replace ( definition , name = name , namespace = namespace ) . to_dict ( )
k8s_obj = resource . replace ( definition , name = name , namespace = namespace ) . to_dict ( )
except DynamicApiError as exc :
except DynamicApiError as exc :
self . fail_json ( msg = " Failed to replace object: {0} " . format ( exc . body ) ,
msg = " Failed to replace object: {0} " . format ( exc . body )
error = exc . status , status = exc . status , reason = exc . reason )
if self . warnings :
msg + = " \n " + " \n " . join ( self . warnings )
self . fail_json ( msg = msg , error = exc . status , status = exc . status , reason = exc . reason )
match , diffs = self . diff_objects ( existing . to_dict ( ) , k8s_obj )
success = True
success = True
result [ ' result ' ] = k8s_obj
result [ ' result ' ] = k8s_obj
if wait :
if wait :
@ -238,11 +279,7 @@ class KubernetesRawModule(KubernetesAnsibleModule):
if self . check_mode :
if self . check_mode :
k8s_obj = dict_merge ( existing . to_dict ( ) , definition )
k8s_obj = dict_merge ( existing . to_dict ( ) , definition )
else :
else :
from distutils . version import LooseVersion
if LooseVersion ( self . openshift_version ) < LooseVersion ( " 0.6.2 " ) :
if LooseVersion ( self . openshift_version ) < LooseVersion ( " 0.6.2 " ) :
if self . params [ ' merge_type ' ] :
self . fail_json ( msg = " openshift >= 0.6.2 is required for merge_type " )
else :
k8s_obj , error = self . patch_resource ( resource , definition , existing , name ,
k8s_obj , error = self . patch_resource ( resource , definition , existing , name ,
namespace )
namespace )
else :
else :
@ -278,8 +315,10 @@ class KubernetesRawModule(KubernetesAnsibleModule):
error = { }
error = { }
return k8s_obj , { }
return k8s_obj , { }
except DynamicApiError as exc :
except DynamicApiError as exc :
error = dict ( msg = " Failed to patch object: {0} " . format ( exc . body ) ,
msg = " Failed to patch object: {0} " . format ( exc . body )
error = exc . status , status = exc . status , reason = exc . reason )
if self . warnings :
msg + = " \n " + " \n " . join ( self . warnings )
error = dict ( msg = msg , error = exc . status , status = exc . status , reason = exc . reason , warnings = self . warnings )
return None , error
return None , error
def create_project_request ( self , definition ) :
def create_project_request ( self , definition ) :