o
    S`M                     @   sP  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-d. Z'd/d0 Z(d1d2 Z)d3d4 Z*d5d6 Z+d7d8 Z,d9d: Z-d;d< Z.d=d> Z/d?d@ Z0dAdB Z1dCdD Z2dEdF Z3dGdH Z4dIdJ Z5dKdL Z6dMdN Z7dOdP Z8dQdR Z9dSdT Z:dUdV Z;dWdX Z<dYdZ Z=d[d\ Z>d]d^ Z?d_d` Z@dadb ZAdcdd ZBdedf ZCdgdh ZDdidj ZEdkdl ZFdS )m    N)
split_port)Draft4Validator)FormatChecker)RefResolver)ValidationError   )COMPOSEFILE_V1)NANOCPUS_SCALE   ConfigurationError)VERSION_EXPLANATION)"get_service_name_from_network_mode
cpu_sharesextra_hostsdeviceslinksmemswap_limitports
privilegedvolumesworking_dir)	cpu_shareadd_hosthosts
extra_hostdevicelinkmemory_swapport	privilege
priviliged	priviligevolume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})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])
$
)IPV6_SEGr&   )formatraisesc              
   C   s4   zt |  W dS  ty } ztt|d }~ww )NT)r   
ValueErrorr   str)instancee r/   ;/usr/lib/python3/dist-packages/compose/config/validation.pyformat_portsE   s   
r1   exposec                 C   s"   t | trtt| stddS )Nz)should be of the format 'PORT[/PROTOCOL]'T)
isinstancer,   rematchVALID_EXPOSE_FORMATr   r-   r/   r/   r0   format_exposeN   s   
r8   subnet_ip_address)r*   c                 C   s.   t | trtt| stt| stddS )Nzshould use the CIDR formatT)r3   r,   r4   r5   VALID_REGEX_IPV4_CIDRVALID_REGEX_IPV6_CIDRr   r7   r/   r/   r0   format_subnet_ip_addressX   s   

r<   c                 C   sD   |  dg }|D ]}|jr|j|vrtd| |  dqd S )Nr   z^Named volume "{}" is used in service "{}" but no declaration was found in the volumes section.name)getis_named_volumeexternalr   r)   repr)service_dictproject_volumesservice_volumesvolume_specr/   r/   r0   match_named_volumesb   s   rF   c              	   C   s(   t | j}ddddddddd||S )Nmappingarraynumberbooleanstring)dictlistintfloatboolunicoder,   bytes)type__name__r>   )type_	type_namer/   r/   r0   python_type_to_yaml_typen   s   
	rW   c              	   C   s   t |tstdj| |tt|d| D ]+\}}t |ts*tdj| ||dt |ttdfsBt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sectionrS   zWIn file '{filename}', the {section} name {name} must be a quoted string, i.e. '{name}'.)rX   rY   r=   NzFIn file '{filename}', {section} '{name}' must be a mapping not {type}.)rX   rY   r=   rS   )	r3   rL   r   r)   anglicize_json_typerW   itemsr,   rS   )rX   configrY   keyvaluer/   r/   r0   validate_config_section|   s>   



r_   c                 C   s(   t | jtstd| jt| jd S )Nz8Top level object in '{}' needs to be an object not '{}'.)r3   r\   rL   r   r)   rX   rS   )config_filer/   r/   r0   validate_top_level_object   s   ra   c                 C   sP   | j di }| D ]\}}t|tr%|d |d ks%tdj| |dqd S )NulimitssofthardzdService '{s.name}' has invalid ulimit '{ulimit}'. 'soft' value can not be greater than 'hard' value )sulimit)r\   r>   r[   r3   rL   r   r)   )service_configulimit_config
limit_namesoft_hard_valuesr/   r/   r0   validate_ulimits   s   
rk   c                 C   s,   d|  }d|vr|du rt d| dS 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_nameextends_optionsrX   error_prefixr/   r/   r0   validate_extends_file_path   s   rp   c                 C   sT   | j d}|s
d S d| j v rtdt|}|sd S ||vr(tdj| |dd S )Nnetwork_modenetworksz0'network_mode' and 'networks' cannot be combinedzPService '{s.name}' uses the network stack of service '{dep}' which is undefined.re   dep)r\   r>   r   r   r)   )rg   service_namesrq   
dependencyr/   r/   r0   validate_network_mode   s   
rw   c                 C   B   | j d}|s
d S t|}|sd S ||vrtdj| |dd S )NpidzPService '{s.name}' uses the PID namespace of service '{dep}' which is undefined.rs   r\   r>   r   r   r)   )rg   ru   pid_moderv   r/   r/   r0   validate_pid_mode      r|   c                 C   rx   )NipczPService '{s.name}' uses the IPC namespace of service '{dep}' which is undefined.rs   rz   )rg   ru   ipc_moderv   r/   r/   r0   validate_ipc_mode   r}   r   c                 C   s<   | j dg D ]}|dd |vrtdj| |dqd S )Nr   :r   zEService '{s.name}' has a link to service '{link}' which is undefined.)re   r   )r\   r>   splitr   r)   )rg   ru   r   r/   r/   r0   validate_links   s   r   c                 C   s:   | j di }| D ]}||vrtdj| |dqd S )N
depends_onzAService '{s.name}' depends on service '{dep}' which is undefined.rs   )r\   r>   keysr   r)   )rg   ru   depsrv   r/   r/   r0   validate_depends_on   s   r   c                 C   s<   | j d}|s
d S d|vrd|vrtdj| dd S d S )Ncredential_specregistryrl   zQService '{s.name}' is missing 'credential_spec.file' or credential_spec.registry')re   )r\   r>   r   r)   )rg   r   r/   r/   r0   validate_credential_spec   s   r   c                 C   s.   d t| |}|tv r|d t| 7 }|S )Nz&Unsupported config option for {}: '{}'z (did you mean '{}'?))r)   path_stringDOCKER_CONFIG_HINTS)path	error_keymsgr/   r/   r0   get_unsupported_config_msg
  s   r   c                 C   s   |  dr	d|  S d|  S )N)ar.   iouzan za )
startswith)	json_typer/   r/   r0   rZ     s   
rZ   c                 C   s   | dv S )N)zconfig_schema_v1.jsonz#/properties/servicesr/   )	schema_idr/   r/   r0   is_service_dict_schema  s   r   c                 C   s   | j d }t|r| jdkrddd t| jD d tS | jdkrS|dkr0t| }t||S |	drIt| }d	j|d

| j d  tdS | jsUd| jtS d 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)r4   r5   VALID_NAME_CHARS)cr/   r/   r0   <lambda>"  s    z<handle_error_for_schema_with_id.<locals>.<listcomp>.<lambda>)anyfilter).0r   r/   r/   r0   
<listcomp>!  s    z3handle_error_for_schema_with_id.<locals>.<listcomp>r   z#/definitions/serviceconfig_schema_zInvalid top-level property "{key}". Valid top-level sections for this Compose file are: {properties}, and extensions starting with "x-".

{explanation}, 
properties)r]   r   explanationz{}

{})schemar   	validatorr)   rM   r-   r   parse_key_from_error_msgr   r   joinr   r   r   message)errorr   r   invalid_config_keyr/   r/   r0   handle_error_for_schema_with_id  s0   



r   c                 C   s   d }| j }| jdkrd}t| \}}|r|| nO| jdkr'd}t| j}nB| jdkr5d| j}d}n4| jdkrYt| j d	 }d
| j| }d}|| d	||}n| j
rdt| j
}d}n| jrid}|rt|j	t||dS | j S )NoneOfz{path} {msg}rS   z3{path} contains an invalid type, it should be {msg}requiredr   z%{path} is invalid, {msg} is required.dependenciesr   ,z{path} is invalid: {msg}z,when defining '{}' you must set '{}' as wellz{path} value {msg})r   r   )r   r   _parse_oneof_validatorappend!_parse_valid_types_from_validatorvalidator_valuer   rM   r   r)   causer,   r   r   )r   r   
msg_format	error_msg
config_keyrequired_keysr/   r/   r0   handle_generic_error:  s>   






r   c                 C   sF   z	| j dd W S  ty"   | j dd dd d Y S w )N'r
   ( r   )r   r   
IndexErrorstrip)r   r/   r/   r0   r   c  s
   $r   c                 C   s   d dd | D S )N.c                 s   s    | ]
}t |tr|V  qd S r   )r3   r,   )r   r   r/   r/   r0   	<genexpr>k  s    zpath_string.<locals>.<genexpr>)r   )r   r/   r/   r0   r   j     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 {}r   )r3   rM   rZ   lenr)   r   )r   r/   r/   r0   r   n  s   

r   c                 C   s   g }| j D ]m}|jdkrt|\}}t|j|f  S |jdkr'd|jf  S |jdkr9t|}dd|f  S |jdkrP|jrFt|jndd|jf  S |jrgt|jdt	
|jt|jf  S |jd	krr||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   Nr   z!contains unsupported option: '{}'uniqueItemsz;contains non-unique items, please remove duplicates from {}z6contains {}, which is an invalid type, it should be {}rS   z)contains an invalid type, it should be {})contextr   r   r   r   r   r   r)   r-   jsondumpsr   r   r   )r   typesr   _r   r   valid_typesr/   r/   r0   r   }  s8   






r   c                 C   sj   |t kr"d| jv rd| jv rd|S d| jv r"d| jv r"d|S d| jvr1d| jvr3d|S d S d S )NimagebuildzService {} has both an image and build path specified. A service can either be built to image or use an existing image, not both.
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)   )r   rm   versionr/   r/   r0   !process_service_constraint_errors  s   r   c                 C   s0   t | j}d| jv rt| |}|r|S t| |S )Nr   )rM   r   r   r   r   )r   r   r   r/   r/   r0   process_config_schema_errors  s   



r   c                 C   s@   i }|   D ]\}}||t|< t|trt||t|< q|S )zH
        Non-string keys may break validator with patterned fields.
    )r[   r,   r3   rL   keys_to_str)r`   dkvr/   r/   r0   r     s   
r   c                 C   sL   t |}t| j}tg d}t|tt ||d}t||t	| j
 d S )N)r   r2   r9   )resolverformat_checker)load_jsonschemar   r\   r   r   r   get_resolver_pathhandle_errorsiter_errorsr   rX   )r`   r   r   r\   r   r   r/   r/   r0   validate_against_config_schema  s   

r   c                    sB    fdd}t  j}t|d d d }t|| |d  d S )Nc                    s   t |  jS r   )r   r   )errorsr`   rm   r/   r0   handler  s   z-validate_service_constraints.<locals>.handlerdefinitionsconstraintsservice)r   r   r   r   r   )r\   rm   r`   r   r   r   r/   r   r0   validate_service_constraints  s   
r   c                 C   s>   | j d}|s
d S |t }t|tr| stdd S d S )Ncpusz6cpus must have nine or less digits after decimal point)r\   r>   r	   r3   rO   
is_integerr   )rg   r   	nano_cpusr/   r/   r0   validate_cpu  s   r   c                   C   s   t jt jtS r   )osr   dirnameabspath__file__r/   r/   r/   r0   get_schema_path  r   r   c                 C   sx   d}| t krd}tjt d|}tj|s!td|tt	|}t
|W  d    S 1 s5w   Y  d S )Ncompose_specconfig_schema_v1z{}.jsonz"Version in "{}" is unsupported. {})r   r   r   r   r   r)   existsr   r   openr   load)r   r=   rX   fhr/   r/   r0   r     s   
$r   c                  C   s2   t  } tjdkrd}| dd} nd}d|| S )Nwin32z///\/z//z
file:{}{}/)r   sysplatformreplacer)   )schema_pathschemer/   r/   r0   r   	  s   
r   c                    sL   t | td} | s
dS d fdd| D }tdj|r d|nd|d	)
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.
    )r]   N
c                 3   s    | ]} |V  qd S r   r/   )r   r   format_error_funcr/   r0   r     s    z handle_errors.<locals>.<genexpr>z:The Compose file{file_msg} is invalid because:
{error_msg}z '{}'r'   )file_msgr   )sortedr,   r   r   r)   )r   r   rX   r   r/   r   r0   r     s   r   c                 C   s   | j di }d|v rHt|d trJt|d dkr"td| j|d d dkr8t|dkr8td| j|d d dvrLtd	| jd S d S d S )
Nhealthcheck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  CMDz	CMD-SHELLzwService "{}" defines an invalid healthcheck: when "test" is a list the first item must be either NONE, CMD or CMD-SHELL)r\   r>   r3   rM   r   r   r)   r=   )rg   r  r/   r/   r0   validate_healthcheck$  s&   r  )Gr   loggingr   r4   r   docker.utils.portsr   
jsonschemar   r   r   r   constr   r   r	   r   r   r   sort_servicesr   	getLoggerrT   logr   r   r6   VALID_IPV4_SEGr)   VALID_IPV4_ADDRr:   VALID_IPV6_SEGr   r   r;   
cls_checksr1   r8   r<   rF   rW   r_   ra   rk   rp   rw   r|   r   r   r   r   r   rZ   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r/   r/   r/   r0   <module>   s    


	
	
))

