U
    ]1K                     @   s`  d dl mZ d dl mZ d dlZd dlZd dlZd dlZd dlZd dlZd dl	m
Z
 d dlmZ d dlmZ d dlmZ d dlmZ d	d
lmZ d	dlmZ ddlmZ ddlmZ ddlmZ eeZddddddddddddddZdZdZdZdj edZ!dj e!d Z"d!Z#d"$d#j e#e!d$% Z&ej'ded%d&d' Z(ej'd(ed%d)d* Z)ej'd+ed,d-d. Z*d/d0 Z+d1d2 Z,d3d4 Z-d5d6 Z.d7d8 Z/d9d: Z0d;d< Z1d=d> Z2d?d@ Z3dAdB Z4dCdD Z5dEdF Z6dGdH Z7dIdJ Z8dKdL Z9dMdN Z:dOdP Z;dQdR Z<dSdT Z=dUdV Z>dWdX Z?dYdZ Z@d[d\ ZAd]d^ ZBd_d` ZCdadb ZDdcdd ZEdedf ZFdgdh ZGdidj ZHdS )k    )absolute_import)unicode_literalsN)
split_port)Draft4Validator)FormatChecker)RefResolver)ValidationError   )COMPOSEFILE_V1)NANOCPUS_SCALE   ConfigurationError)VERSION_EXPLANATION)"get_service_name_from_network_modeZ
cpu_sharesZextra_hostsZdeviceslinksZmemswap_limitportsZ
privilegedvolumesZworking_dir)Z	cpu_shareZadd_hostZhostsZ
extra_hostZdevicelinkZmemory_swapZportZ	privilegeZ
priviligedZ	priviligeZvolumeZworkdirz[a-zA-Z0-9\._\-]z^\d+(\-\d+)?(\/[a-zA-Z]+)?$z!(\d{1,2}|1\d{2}|2[0-4]\d|25[0-5])z({IPV4_SEG}\.){{3}}{IPV4_SEG})ZIPV4_SEGz!^{IPV4_ADDR}/(\d|[1-2]\d|3[0-2])$)	IPV4_ADDRz[0-9a-fA-F]{1,4} a9  
^
(
    (({IPV6_SEG}:){{7}}{IPV6_SEG})|
    (({IPV6_SEG}:){{1,7}}:)|
    (({IPV6_SEG}:){{1,6}}(:{IPV6_SEG}){{1,1}})|
    (({IPV6_SEG}:){{1,5}}(:{IPV6_SEG}){{1,2}})|
    (({IPV6_SEG}:){{1,4}}(:{IPV6_SEG}){{1,3}})|
    (({IPV6_SEG}:){{1,3}}(:{IPV6_SEG}){{1,4}})|
    (({IPV6_SEG}:){{1,2}}(:{IPV6_SEG}){{1,5}})|
    (({IPV6_SEG}:){{1,1}}(:{IPV6_SEG}){{1,6}})|
    (:((:{IPV6_SEG}){{1,7}}|:))|
    (fe80:(:{IPV6_SEG}){{0,4}}%[0-9a-zA-Z]{{1,}})|
    (::(ffff(:0{{1,4}}){{0,1}}:){{0,1}}{IPV4_ADDR})|
    (({IPV6_SEG}:){{1,4}}:{IPV4_ADDR})
)
/(\d|[1-9]\d|1[0-1]\d|12[0-8])
$
)ZIPV6_SEGr   )formatraisesc              
   C   sB   zt |  W n0 tk
r< } ztt|W 5 d }~X Y nX dS )NT)r   
ValueErrorr   six	text_type)instancee r   ;/usr/lib/python3/dist-packages/compose/config/validation.pyformat_portsI   s
     r    exposec                 C   s$   t | tjr tt| s tddS )Nz)should be of the format 'PORT[/PROTOCOL]'T)
isinstancer   string_typesrematchVALID_EXPOSE_FORMATr   r   r   r   r   format_exposeR   s    r(   subnet_ip_address)r   c                 C   s0   t | tjr,tt| s,tt| s,tddS )Nzshould use the CIDR formatT)r"   r   r#   r$   r%   VALID_REGEX_IPV4_CIDRVALID_REGEX_IPV6_CIDRr   r'   r   r   r   format_subnet_ip_address\   s    
r,   c                 C   sD   |  dg }|D ].}|jr|j|krtd| |  dqd S )Nr   z`Named volume "{0}" is used in service "{1}" but no declaration was found in the volumes section.name)getZis_named_volumeZexternalr   r   repr)Zservice_dictZproject_volumesZservice_volumesZvolume_specr   r   r   match_named_volumesf   s     r0   c              	   C   s(   t | j}ddddddddd||S )NmappingZarrayZnumberZbooleanstring)dictlistintfloatboolZunicodestrbytes)type__name__r.   )Ztype_Z	type_namer   r   r   python_type_to_yaml_typer   s    
	 r<   c              	   C   s   t |ts&tdj| |tt|d| D ]X\}}t |tjsVtdj| ||dt |tt	dfs.tdj| ||tt|dq.dS )zValidate the structure of a configuration section. This must be done
    before interpolation so it's separate from schema validation.
    z>In file '{filename}', {section} must be a mapping, not {type}.)filenamesectionr:   zWIn file '{filename}', the {section} name {name} must be a quoted string, i.e. '{name}'.)r=   r>   r-   NzFIn file '{filename}', {section} '{name}' must be a mapping not {type}.)r=   r>   r-   r:   )
r"   r3   r   r   anglicize_json_typer<   itemsr   r#   r:   )r=   configr>   keyvaluer   r   r   validate_config_section   s4    


rD   c                 C   s(   t | jts$td| jt| jd S )Nz8Top level object in '{}' needs to be an object not '{}'.)r"   rA   r3   r   r   r=   r:   )config_filer   r   r   validate_top_level_object   s    rF   c                 C   sR   | j di }t|D ]4\}}t|tr|d |d kstdj| |dqd S )NZulimitsZsoftZhardzdService '{s.name}' has invalid ulimit '{ulimit}'. 'soft' value can not be greater than 'hard' value )sZulimit)rA   r.   r   Z	iteritemsr"   r3   r   r   )service_configZulimit_configZ
limit_nameZsoft_hard_valuesr   r   r   validate_ulimits   s    
rI   c                 C   s(   d|  }d|kr$|dkr$t d| dS )zo
    The service to be extended must either be defined in the config key 'file',
    or within 'filename'.
    z'Invalid 'extends' configuration for %s:fileNz;%s you need to specify a 'file', e.g. 'file: something.yml'r   )service_nameZextends_optionsr=   Zerror_prefixr   r   r   validate_extends_file_path   s
    rL   c                 C   sT   | j d}|sd S d| j kr&tdt|}|s6d S ||krPtdj| |dd S )Nnetwork_modeZnetworksz0'network_mode' and 'networks' cannot be combinedzPService '{s.name}' uses the network stack of service '{dep}' which is undefined.rG   Zdep)rA   r.   r   r   r   )rH   service_namesrM   
dependencyr   r   r   validate_network_mode   s    
 rQ   c                 C   sB   | j d}|sd S t|}|s$d S ||kr>tdj| |dd S )NpidzPService '{s.name}' uses the PID namespace of service '{dep}' which is undefined.rN   )rA   r.   r   r   r   )rH   rO   Zpid_moderP   r   r   r   validate_pid_mode   s     rS   c                 C   s<   | j dg D ](}|dd |krtdj| |dqd S )Nr   :r   zEService '{s.name}' has a link to service '{link}' which is undefined.)rG   r   )rA   r.   splitr   r   )rH   rO   r   r   r   r   validate_links   s     rV   c                 C   s:   | j di }| D ]}||krtdj| |dqd S )NZ
depends_onzAService '{s.name}' depends on service '{dep}' which is undefined.rN   )rA   r.   keysr   r   )rH   rO   ZdepsrP   r   r   r   validate_depends_on   s     rX   c                 C   s8   | j d}|sd S d|kr4d|kr4tdj| dd S )Ncredential_specregistryrJ   zQService '{s.name}' is missing 'credential_spec.file' or credential_spec.registry')rG   )rA   r.   r   r   )rH   rY   r   r   r   validate_credential_spec   s    r[   c                 C   s.   d t| |}|tkr*|d t| 7 }|S )Nz&Unsupported config option for {}: '{}'z (did you mean '{}'?))r   path_stringDOCKER_CONFIG_HINTS)pathZ	error_keymsgr   r   r   get_unsupported_config_msg   s    r`   c                 C   s   |  drd|  S d|  S )N)ar   iouzan za )
startswith)Z	json_typer   r   r   r?     s    
r?   c                 C   s   | dkS )N)zconfig_schema_v1.jsonz#/properties/servicesr   )	schema_idr   r   r   is_service_dict_schema  s    rg   c                 C   s   | j d }t|r<| jdkr<ddd t| jD d tS | jdkr|dkr`t| }t||S |	drt| }d	j|d

| j d  tdS | jsd| jtS d S )NidadditionalPropertiesz:Invalid service name '{}' - only {} characters are allowedc                 S   s&   g | ]}|rt td d |r|qS )c                 S   s   t t|  S N)r$   r%   VALID_NAME_CHARS)cr   r   r   <lambda>      z<handle_error_for_schema_with_id.<locals>.<listcomp>.<lambda>)anyfilter).0rb   r   r   r   
<listcomp>  s        z3handle_error_for_schema_with_id.<locals>.<listcomp>r   z#/definitions/serviceZconfig_schema_vzInvalid top-level property "{key}". Valid top-level sections for this Compose file are: {properties}, and extensions starting with "x-".

{explanation}, 
properties)rB   rt   Zexplanationz{}

{})schemarg   	validatorr   r4   r   rk   parse_key_from_error_msgr`   re   joinrW   r   r^   message)errorr^   rf   invalid_config_keyr   r   r   handle_error_for_schema_with_id  s*    



r|   c                 C   s   d }| j }| jdkr4d}t| \}}|r|| n| jdkrNd}t| j}n| jdkrjd| j}d}nj| jdkrt| j d	 }d
| j| }d}|| d	||}n"| j
rt| j
}d}n
| jrd}|r|j	t||dS | j S )NoneOfz{path} {msg}r:   z3{path} contains an invalid type, it should be {msg}requiredrs   z%{path} is invalid, {msg} is required.Zdependenciesr   ,z{path} is invalid: {msg}z,when defining '{}' you must set '{}' as wellz{path} value {msg})r^   r_   )ry   rv   _parse_oneof_validatorappend!_parse_valid_types_from_validatorvalidator_valuerx   r4   rW   r   causer   r   r^   r\   )rz   r^   Z
msg_format	error_msgZ
config_keyZrequired_keysr   r   r   handle_generic_error/  s<    




r   c                 C   sL   z| j dd W S  tk
rF   | j dd dd d Y S X d S )N'r   ( r   )ry   rU   
IndexErrorstrip)rz   r   r   r   rw   X  s    rw   c                 C   s   d dd | D S )N.c                 s   s   | ]}t |tjr|V  qd S rj   )r"   r   r#   )rq   rl   r   r   r   	<genexpr>`  s      zpath_string.<locals>.<genexpr>)rx   )r^   r   r   r   r\   _  s    r\   c                 C   sZ   t | tst| S t| dkr*t| d S ddt| d g| dd  t| d S )zA validator value can be either an array of valid types or a string of
    a valid type. Parse the valid types and prefix with the correct article.
    r   r   z	{}, or {}rs   )r"   r4   r?   lenr   rx   )rv   r   r   r   r   c  s    

r   c                 C   s   g }| j D ]}|jdkr6t|\}}t|j|f  S |jdkrNd|jf  S |jdkrrt|}dd|f  S |jdkr|jrt|jndd|jf  S |jrt|jdt	
|jt|jf  S |jd	kr
||j q
t|}dd
|fS )a  oneOf has multiple schemas, so we need to reason about which schema, sub
    schema or constraint the validation is failing on.
    Inspecting the context value of a ValidationError gives us information about
    which sub schema failed and which kind of error it is.
    r}   r~   Nri   z!contains unsupported option: '{}'ZuniqueItemsz;contains non-unique items, please remove duplicates from {}z6contains {}, which is an invalid type, it should be {}r:   z)contains an invalid type, it should be {})contextrv   r   r\   r^   ry   rw   r   r   jsondumpsr   r   r   )rz   typesr   _r   r{   Zvalid_typesr   r   r   r   r  s6    






r   c                 C   sf   |t krDd| jkr&d| jkr&d|S d| jkrDd| jkrDd|S d| jkrbd| jkrbd|S d S )NZimageZbuildzService {} has both an image and build path specified. A service can either be built to image or use an existing image, not both.Z
dockerfilezService {} has both an image and alternate Dockerfile. A service can either be built to image or use an existing image, not both.z]Service {} has neither an image nor a build context specified. At least one must be provided.)V1r   r   )rz   rK   versionr   r   r   !process_service_constraint_errors  s     r   c                 C   s0   t | j}d| jkr&t| |}|r&|S t| |S )Nrh   )r4   r^   ru   r|   r   )rz   r^   r   r   r   r   process_config_schema_errors  s    


r   c                 C   sF   t | }tdddg}t|tt ||d}t|| jt| j	 d S )Nr   r!   r)   )Zresolverformat_checker)
load_jsonschemar   r   r   get_resolver_pathhandle_errorsiter_errorsrA   r   r=   )rE   ru   r   rv   r   r   r   validate_against_config_schema  s    

r   c                    s@    fdd}t  }t|d d d }t|| |d  d S )Nc                    s   t |  jS rj   )r   r   )errorsrE   rK   r   r   handler  s
      z-validate_service_constraints.<locals>.handlerZdefinitionsZconstraintsservice)r   r   r   r   )rA   rK   rE   r   ru   rv   r   r   r   validate_service_constraints  s    r   c                 C   s:   | j d}|sd S |t }t|tr6| s6tdd S )Ncpusz6cpus must have nine or less digits after decimal point)rA   r.   r   r"   r6   
is_integerr   )rH   r   Z	nano_cpusr   r   r   validate_cpu  s    r   c                   C   s   t jt jtS rj   )osr^   dirnameabspath__file__r   r   r   r   get_schema_path  s    r   c              
   C   sb   t jt d| j}t j|s6td| jt	t
|d}t|W  5 Q R  S Q R X d S )Nzconfig_schema_v{0}.jsonz"Version in "{}" is unsupported. {}r)r   r^   rx   r   r   r   existsr   r=   r   openr   load)rE   r=   Zfhr   r   r   r     s    
 r   c                  C   s2   t  } tjdkr"d}| dd} nd}d|| S )NZwin32z///\/z//z
file:{}{}/)r   sysplatformreplacer   )Zschema_pathZschemer   r   r   r     s    
r   c                    sT   t t| td} | sdS d fdd| D }tdj|rDd|nd|d	dS )
zjsonschema returns an error tree full of information to explain what has
    gone wrong. Process each error and pull out relevant information and re-write
    helpful error messages that are relevant.
    )rB   N
c                 3   s   | ]} |V  qd S rj   r   )rq   rz   format_error_funcr   r   r     s     z handle_errors.<locals>.<genexpr>z:The Compose file{file_msg} is invalid because:
{error_msg}z '{}'r   )Zfile_msgr   )r4   sortedr8   rx   r   r   )r   r   r=   r   r   r   r   r     s    r   c                 C   s   | j di }d|krt|d trt|d dkrFtd| jnN|d d dkrtt|dkrttd| jn |d d dkrtd	| jd S )
NhealthcheckZtestr   zDService "{}" defines an invalid healthcheck: "test" is an empty listNONEr   zbService "{}" defines an invalid healthcheck: "disable: true" cannot be combined with other options)r   ZCMDz	CMD-SHELLzwService "{}" defines an invalid healthcheck: when "test" is a list the first item must be either NONE, CMD or CMD-SHELL)rA   r.   r"   r4   r   r   r   r-   )rH   r   r   r   r   validate_healthcheck  s(    r   )IZ
__future__r   r   r   Zloggingr   r$   r   r   Zdocker.utils.portsr   Z
jsonschemar   r   r   r   Zconstr
   r   r   r   r   r   Zsort_servicesr   Z	getLoggerr;   logr]   rk   r&   ZVALID_IPV4_SEGr   ZVALID_IPV4_ADDRr*   ZVALID_IPV6_SEGrx   rU   r+   Z
cls_checksr    r(   r,   r0   r<   rD   rF   rI   rL   rQ   rS   rV   rX   r[   r`   r?   rg   r|   r   rw   r\   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>   s   
 

	
	
))

