Add httpHeaders to httpGet liveness probe

Also update existing documentation and try to steer users away from 'host'.
Add validation.
pull/6/head
Rudi Chiarito 2016-02-02 10:03:50 -05:00
parent faa0fc3d8c
commit a2d1bb7acf
27 changed files with 30025 additions and 28798 deletions

View File

@ -15902,11 +15902,36 @@
},
"host": {
"type": "string",
"description": "Host name to connect to, defaults to the pod IP."
"description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead."
},
"scheme": {
"type": "string",
"description": "Scheme to use for connecting to the host. Defaults to HTTP."
},
"httpHeaders": {
"type": "array",
"items": {
"$ref": "v1.HTTPHeader"
},
"description": "Custom headers to set in the request. HTTP allows repeated headers."
}
}
},
"v1.HTTPHeader": {
"id": "v1.HTTPHeader",
"description": "HTTPHeader describes a custom header to be used in HTTP probes",
"required": [
"name",
"value"
],
"properties": {
"name": {
"type": "string",
"description": "The header field name"
},
"value": {
"type": "string",
"description": "The header field value"
}
}
},

View File

@ -4781,11 +4781,36 @@
},
"host": {
"type": "string",
"description": "Host name to connect to, defaults to the pod IP."
"description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead."
},
"scheme": {
"type": "string",
"description": "Scheme to use for connecting to the host. Defaults to HTTP."
},
"httpHeaders": {
"type": "array",
"items": {
"$ref": "v1.HTTPHeader"
},
"description": "Custom headers to set in the request. HTTP allows repeated headers."
}
}
},
"v1.HTTPHeader": {
"id": "v1.HTTPHeader",
"description": "HTTPHeader describes a custom header to be used in HTTP probes",
"required": [
"name",
"value"
],
"properties": {
"name": {
"type": "string",
"description": "The header field name"
},
"value": {
"type": "string",
"description": "The header field value"
}
}
},

View File

@ -893,6 +893,47 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_httpheader">v1.HTTPHeader</h3>
<div class="paragraph">
<p>HTTPHeader describes a custom header to be used in HTTP probes</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The header field name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">value</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The header field value</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1beta1_horizontalpodautoscalerstatus">v1beta1.HorizontalPodAutoscalerStatus</h3>
@ -1562,7 +1603,7 @@ Both these may change in the future. Incoming requests are matched against the h
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">host</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Host name to connect to, defaults to the pod IP.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
@ -1574,6 +1615,13 @@ Both these may change in the future. Incoming requests are matched against the h
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">httpHeaders</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom headers to set in the request. HTTP allows repeated headers.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_httpheader">v1.HTTPHeader</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
@ -4838,7 +4886,7 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
</div>
<div id="footer">
<div id="footer-text">
Last updated 2016-02-05 15:52:54 UTC
Last updated 2016-02-05 16:19:07 UTC
</div>
</div>
</body>

View File

@ -3432,6 +3432,89 @@ The resulting set of endpoints can be viewed as:<br>
<div class="sect2">
<h3 id="_v1_capability">v1.Capability</h3>
</div>
<div class="sect2">
<h3 id="_v1_podstatus">v1.PodStatus</h3>
<div class="paragraph">
<p>PodStatus represents information about the status of a pod. Status may trail the actual state of a system.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">phase</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Current condition of the pod. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-phase">http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-phase</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">conditions</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Current service state of pod. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-conditions">http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-conditions</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_podcondition">v1.PodCondition</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">message</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">A human readable message indicating details about why the pod is in this condition.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">reason</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">A brief CamelCase message indicating details about why the pod is in this state. e.g. <em>OutOfDisk</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">hostIP</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">IP address of the host to which the pod is assigned. Empty if not yet scheduled.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">podIP</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">IP address allocated to the pod. Routable at least within the cluster. Empty if not yet allocated.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">startTime</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">RFC 3339 date and time at which the object was acknowledged by the Kubelet. This is before the Kubelet pulled the container image(s) for the pod.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">containerStatuses</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The list has one entry per container in the manifest. Each entry is currently the output of <code>docker inspect</code>. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-statuses">http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-statuses</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_containerstatus">v1.ContainerStatus</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_limitrange">v1.LimitRange</h3>
@ -3528,89 +3611,6 @@ The resulting set of endpoints can be viewed as:<br>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_podstatus">v1.PodStatus</h3>
<div class="paragraph">
<p>PodStatus represents information about the status of a pod. Status may trail the actual state of a system.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">phase</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Current condition of the pod. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-phase">http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-phase</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">conditions</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Current service state of pod. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-conditions">http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-conditions</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_podcondition">v1.PodCondition</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">message</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">A human readable message indicating details about why the pod is in this condition.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">reason</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">A brief CamelCase message indicating details about why the pod is in this state. e.g. <em>OutOfDisk</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">hostIP</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">IP address of the host to which the pod is assigned. Empty if not yet scheduled.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">podIP</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">IP address allocated to the pod. Routable at least within the cluster. Empty if not yet allocated.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">startTime</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">RFC 3339 date and time at which the object was acknowledged by the Kubelet. This is before the Kubelet pulled the container image(s) for the pod.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">containerStatuses</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The list has one entry per container in the manifest. Each entry is currently the output of <code>docker inspect</code>. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-statuses">http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-statuses</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_containerstatus">v1.ContainerStatus</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_podspec">v1.PodSpec</h3>
@ -4520,6 +4520,47 @@ The resulting set of endpoints can be viewed as:<br>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_httpheader">v1.HTTPHeader</h3>
<div class="paragraph">
<p>HTTPHeader describes a custom header to be used in HTTP probes</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The header field name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">value</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The header field value</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_fcvolumesource">v1.FCVolumeSource</h3>
@ -4759,7 +4800,7 @@ The resulting set of endpoints can be viewed as:<br>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">host</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Host name to connect to, defaults to the pod IP.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
@ -4771,6 +4812,13 @@ The resulting set of endpoints can be viewed as:<br>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">httpHeaders</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom headers to set in the request. HTTP allows repeated headers.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_httpheader">v1.HTTPHeader</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
@ -7268,7 +7316,7 @@ The resulting set of endpoints can be viewed as:<br>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2016-02-01 21:08:06 UTC
Last updated 2016-02-02 15:03:04 UTC
</div>
</div>
</body>

View File

@ -66,11 +66,14 @@ The [http-liveness.yaml](http-liveness.yaml) demonstrates the HTTP check.
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: X-Custom-Header
value: Awesome
initialDelaySeconds: 15
timeoutSeconds: 1
```
The Kubelet sends an HTTP request to the specified path and port to perform the health check. If you take a look at image/server.go, you will see the server starts to respond with an error code 500 after 10 seconds, so the check fails. The Kubelet sends the probe to the container's ip address by default which could be specified with `host` as part of httpGet probe. If the container listens on `127.0.0.1`, `host` should be specified as `127.0.0.1`. In general, if the container listens on its ip address or on all interfaces (0.0.0.0), there is no need to specify the `host` as part of the httpGet probe.
The Kubelet sends an HTTP request to the specified path and port to perform the health check. If you take a look at image/server.go, you will see the server starts to respond with an error code 500 after 10 seconds, so the check fails. The Kubelet sends probes to the container's IP address, unless overridden by the optional `host` field in httpGet. If the container listens on `127.0.0.1` and `hostNetwork` is `true` (i.e., it does not use the pod-specific network), then `host` should be specified as `127.0.0.1`. Be warned that, outside of less common cases like that, `host` does probably not result in what you would expect. If you set it to a non-existing hostname (or your competitor's!), probes will never reach the pod, defeating the whole point of health checks. If your pod relies on e.g. virtual hosts, which is probably the more common case, you should not use `host`, but rather set the `Host` header in `httpHeaders`.
This [guide](../walkthrough/k8s201.md#health-checking) has more information on health checks.

View File

@ -13,6 +13,9 @@ spec:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: X-Custom-Header
value: Awesome
initialDelaySeconds: 15
timeoutSeconds: 1
name: liveness

View File

@ -76,6 +76,7 @@ func init() {
DeepCopy_api_GitRepoVolumeSource,
DeepCopy_api_GlusterfsVolumeSource,
DeepCopy_api_HTTPGetAction,
DeepCopy_api_HTTPHeader,
DeepCopy_api_Handler,
DeepCopy_api_HostPathVolumeSource,
DeepCopy_api_ISCSIVolumeSource,
@ -930,6 +931,23 @@ func DeepCopy_api_HTTPGetAction(in HTTPGetAction, out *HTTPGetAction, c *convers
}
out.Host = in.Host
out.Scheme = in.Scheme
if in.HTTPHeaders != nil {
in, out := in.HTTPHeaders, &out.HTTPHeaders
*out = make([]HTTPHeader, len(in))
for i := range in {
if err := DeepCopy_api_HTTPHeader(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.HTTPHeaders = nil
}
return nil
}
func DeepCopy_api_HTTPHeader(in HTTPHeader, out *HTTPHeader, c *conversion.Cloner) error {
out.Name = in.Name
out.Value = in.Value
return nil
}

File diff suppressed because it is too large Load Diff

View File

@ -752,16 +752,27 @@ type SecretKeySelector struct {
Key string `json:"key"`
}
// HTTPHeader describes a custom header to be used in HTTP probes
type HTTPHeader struct {
// The header field name
Name string `json:"name"`
// The header field value
Value string `json:"value"`
}
// HTTPGetAction describes an action based on HTTP Get requests.
type HTTPGetAction struct {
// Optional: Path to access on the HTTP server.
Path string `json:"path,omitempty"`
// Required: Name or number of the port to access on the container.
Port intstr.IntOrString `json:"port,omitempty"`
// Optional: Host name to connect to, defaults to the pod IP.
// Optional: Host name to connect to, defaults to the pod IP. You
// probably want to set "Host" in httpHeaders instead.
Host string `json:"host,omitempty"`
// Optional: Scheme to use for connecting to the host, defaults to HTTP.
Scheme URIScheme `json:"scheme,omitempty"`
// Optional: Custom headers to set in the request. HTTP allows repeated headers.
HTTPHeaders []HTTPHeader `json:"httpHeaders,omitempty"`
}
// URIScheme identifies the scheme used for connection to a host for Get actions

View File

@ -977,6 +977,16 @@ func autoConvert_api_HTTPGetAction_To_v1_HTTPGetAction(in *api.HTTPGetAction, ou
}
out.Host = in.Host
out.Scheme = URIScheme(in.Scheme)
if in.HTTPHeaders != nil {
out.HTTPHeaders = make([]HTTPHeader, len(in.HTTPHeaders))
for i := range in.HTTPHeaders {
if err := Convert_api_HTTPHeader_To_v1_HTTPHeader(&in.HTTPHeaders[i], &out.HTTPHeaders[i], s); err != nil {
return err
}
}
} else {
out.HTTPHeaders = nil
}
return nil
}
@ -984,6 +994,19 @@ func Convert_api_HTTPGetAction_To_v1_HTTPGetAction(in *api.HTTPGetAction, out *H
return autoConvert_api_HTTPGetAction_To_v1_HTTPGetAction(in, out, s)
}
func autoConvert_api_HTTPHeader_To_v1_HTTPHeader(in *api.HTTPHeader, out *HTTPHeader, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*api.HTTPHeader))(in)
}
out.Name = in.Name
out.Value = in.Value
return nil
}
func Convert_api_HTTPHeader_To_v1_HTTPHeader(in *api.HTTPHeader, out *HTTPHeader, s conversion.Scope) error {
return autoConvert_api_HTTPHeader_To_v1_HTTPHeader(in, out, s)
}
func autoConvert_api_Handler_To_v1_Handler(in *api.Handler, out *Handler, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*api.Handler))(in)
@ -4155,6 +4178,16 @@ func autoConvert_v1_HTTPGetAction_To_api_HTTPGetAction(in *HTTPGetAction, out *a
}
out.Host = in.Host
out.Scheme = api.URIScheme(in.Scheme)
if in.HTTPHeaders != nil {
out.HTTPHeaders = make([]api.HTTPHeader, len(in.HTTPHeaders))
for i := range in.HTTPHeaders {
if err := Convert_v1_HTTPHeader_To_api_HTTPHeader(&in.HTTPHeaders[i], &out.HTTPHeaders[i], s); err != nil {
return err
}
}
} else {
out.HTTPHeaders = nil
}
return nil
}
@ -4162,6 +4195,19 @@ func Convert_v1_HTTPGetAction_To_api_HTTPGetAction(in *HTTPGetAction, out *api.H
return autoConvert_v1_HTTPGetAction_To_api_HTTPGetAction(in, out, s)
}
func autoConvert_v1_HTTPHeader_To_api_HTTPHeader(in *HTTPHeader, out *api.HTTPHeader, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*HTTPHeader))(in)
}
out.Name = in.Name
out.Value = in.Value
return nil
}
func Convert_v1_HTTPHeader_To_api_HTTPHeader(in *HTTPHeader, out *api.HTTPHeader, s conversion.Scope) error {
return autoConvert_v1_HTTPHeader_To_api_HTTPHeader(in, out, s)
}
func autoConvert_v1_Handler_To_api_Handler(in *Handler, out *api.Handler, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*Handler))(in)
@ -6279,6 +6325,7 @@ func init() {
autoConvert_api_GitRepoVolumeSource_To_v1_GitRepoVolumeSource,
autoConvert_api_GlusterfsVolumeSource_To_v1_GlusterfsVolumeSource,
autoConvert_api_HTTPGetAction_To_v1_HTTPGetAction,
autoConvert_api_HTTPHeader_To_v1_HTTPHeader,
autoConvert_api_Handler_To_v1_Handler,
autoConvert_api_HostPathVolumeSource_To_v1_HostPathVolumeSource,
autoConvert_api_ISCSIVolumeSource_To_v1_ISCSIVolumeSource,
@ -6405,6 +6452,7 @@ func init() {
autoConvert_v1_GitRepoVolumeSource_To_api_GitRepoVolumeSource,
autoConvert_v1_GlusterfsVolumeSource_To_api_GlusterfsVolumeSource,
autoConvert_v1_HTTPGetAction_To_api_HTTPGetAction,
autoConvert_v1_HTTPHeader_To_api_HTTPHeader,
autoConvert_v1_Handler_To_api_Handler,
autoConvert_v1_HostPathVolumeSource_To_api_HostPathVolumeSource,
autoConvert_v1_ISCSIVolumeSource_To_api_ISCSIVolumeSource,

View File

@ -749,6 +749,22 @@ func deepCopy_v1_HTTPGetAction(in HTTPGetAction, out *HTTPGetAction, c *conversi
}
out.Host = in.Host
out.Scheme = in.Scheme
if in.HTTPHeaders != nil {
out.HTTPHeaders = make([]HTTPHeader, len(in.HTTPHeaders))
for i := range in.HTTPHeaders {
if err := deepCopy_v1_HTTPHeader(in.HTTPHeaders[i], &out.HTTPHeaders[i], c); err != nil {
return err
}
}
} else {
out.HTTPHeaders = nil
}
return nil
}
func deepCopy_v1_HTTPHeader(in HTTPHeader, out *HTTPHeader, c *conversion.Cloner) error {
out.Name = in.Name
out.Value = in.Value
return nil
}
@ -2576,6 +2592,7 @@ func init() {
deepCopy_v1_GitRepoVolumeSource,
deepCopy_v1_GlusterfsVolumeSource,
deepCopy_v1_HTTPGetAction,
deepCopy_v1_HTTPHeader,
deepCopy_v1_Handler,
deepCopy_v1_HostPathVolumeSource,
deepCopy_v1_ISCSIVolumeSource,

File diff suppressed because it is too large Load Diff

View File

@ -877,6 +877,14 @@ type SecretKeySelector struct {
Key string `json:"key"`
}
// HTTPHeader describes a custom header to be used in HTTP probes
type HTTPHeader struct {
// The header field name
Name string `json:"name"`
// The header field value
Value string `json:"value"`
}
// HTTPGetAction describes an action based on HTTP Get requests.
type HTTPGetAction struct {
// Path to access on the HTTP server.
@ -885,11 +893,14 @@ type HTTPGetAction struct {
// Number must be in the range 1 to 65535.
// Name must be an IANA_SVC_NAME.
Port intstr.IntOrString `json:"port"`
// Host name to connect to, defaults to the pod IP.
// Host name to connect to, defaults to the pod IP. You probably want to set
// "Host" in httpHeaders instead.
Host string `json:"host,omitempty"`
// Scheme to use for connecting to the host.
// Defaults to HTTP.
Scheme URIScheme `json:"scheme,omitempty"`
// Custom headers to set in the request. HTTP allows repeated headers.
HTTPHeaders []HTTPHeader `json:"httpHeaders,omitempty"`
}
// URIScheme identifies the scheme used for connection to a host for Get actions

View File

@ -509,17 +509,28 @@ func (GlusterfsVolumeSource) SwaggerDoc() map[string]string {
}
var map_HTTPGetAction = map[string]string{
"": "HTTPGetAction describes an action based on HTTP Get requests.",
"path": "Path to access on the HTTP server.",
"port": "Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.",
"host": "Host name to connect to, defaults to the pod IP.",
"scheme": "Scheme to use for connecting to the host. Defaults to HTTP.",
"": "HTTPGetAction describes an action based on HTTP Get requests.",
"path": "Path to access on the HTTP server.",
"port": "Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.",
"host": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
"scheme": "Scheme to use for connecting to the host. Defaults to HTTP.",
"httpHeaders": "Custom headers to set in the request. HTTP allows repeated headers.",
}
func (HTTPGetAction) SwaggerDoc() map[string]string {
return map_HTTPGetAction
}
var map_HTTPHeader = map[string]string{
"": "HTTPHeader describes a custom header to be used in HTTP probes",
"name": "The header field name",
"value": "The header field value",
}
func (HTTPHeader) SwaggerDoc() map[string]string {
return map_HTTPHeader
}
var map_Handler = map[string]string{
"": "Handler defines a specific action that should be taken",
"exec": "One and only one of the following should be specified. Exec specifies the action to take.",

View File

@ -1128,6 +1128,11 @@ func validateHTTPGetAction(http *api.HTTPGetAction, fldPath *field.Path) field.E
if !supportedSchemes.Has(string(http.Scheme)) {
allErrors = append(allErrors, field.Invalid(fldPath.Child("scheme"), http.Scheme, fmt.Sprintf("must be one of %v", supportedSchemes.List())))
}
for _, header := range http.HTTPHeaders {
if !validation.IsHTTPHeaderName(header.Name) {
allErrors = append(allErrors, field.Invalid(fldPath.Child("httpHeaders"), header.Name, fmt.Sprintf("name must match %s", validation.HTTPHeaderNameFmt)))
}
}
return allErrors
}

View File

@ -1082,6 +1082,8 @@ func TestValidateHandler(t *testing.T) {
{HTTPGet: &api.HTTPGetAction{Path: "/", Port: intstr.FromInt(1), Host: "", Scheme: "HTTP"}},
{HTTPGet: &api.HTTPGetAction{Path: "/foo", Port: intstr.FromInt(65535), Host: "host", Scheme: "HTTP"}},
{HTTPGet: &api.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP"}},
{HTTPGet: &api.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP", HTTPHeaders: []api.HTTPHeader{{"Host", "foo.example.com"}}}},
{HTTPGet: &api.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP", HTTPHeaders: []api.HTTPHeader{{"X-Forwarded-For", "1.2.3.4"}, {"X-Forwarded-For", "5.6.7.8"}}}},
}
for _, h := range successCases {
if errs := validateHandler(&h, field.NewPath("field")); len(errs) != 0 {
@ -1095,6 +1097,8 @@ func TestValidateHandler(t *testing.T) {
{HTTPGet: &api.HTTPGetAction{Path: "", Port: intstr.FromInt(0), Host: ""}},
{HTTPGet: &api.HTTPGetAction{Path: "/foo", Port: intstr.FromInt(65536), Host: "host"}},
{HTTPGet: &api.HTTPGetAction{Path: "", Port: intstr.FromString(""), Host: ""}},
{HTTPGet: &api.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP", HTTPHeaders: []api.HTTPHeader{{"Host:", "foo.example.com"}}}},
{HTTPGet: &api.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP", HTTPHeaders: []api.HTTPHeader{{"X_Forwarded_For", "foo.example.com"}}}},
}
for _, h := range errorCases {
if errs := validateHandler(&h, field.NewPath("field")); len(errs) == 0 {

View File

@ -502,6 +502,16 @@ func autoConvert_api_HTTPGetAction_To_v1_HTTPGetAction(in *api.HTTPGetAction, ou
}
out.Host = in.Host
out.Scheme = v1.URIScheme(in.Scheme)
if in.HTTPHeaders != nil {
out.HTTPHeaders = make([]v1.HTTPHeader, len(in.HTTPHeaders))
for i := range in.HTTPHeaders {
if err := Convert_api_HTTPHeader_To_v1_HTTPHeader(&in.HTTPHeaders[i], &out.HTTPHeaders[i], s); err != nil {
return err
}
}
} else {
out.HTTPHeaders = nil
}
return nil
}
@ -509,6 +519,19 @@ func Convert_api_HTTPGetAction_To_v1_HTTPGetAction(in *api.HTTPGetAction, out *v
return autoConvert_api_HTTPGetAction_To_v1_HTTPGetAction(in, out, s)
}
func autoConvert_api_HTTPHeader_To_v1_HTTPHeader(in *api.HTTPHeader, out *v1.HTTPHeader, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*api.HTTPHeader))(in)
}
out.Name = in.Name
out.Value = in.Value
return nil
}
func Convert_api_HTTPHeader_To_v1_HTTPHeader(in *api.HTTPHeader, out *v1.HTTPHeader, s conversion.Scope) error {
return autoConvert_api_HTTPHeader_To_v1_HTTPHeader(in, out, s)
}
func autoConvert_api_Handler_To_v1_Handler(in *api.Handler, out *v1.Handler, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*api.Handler))(in)
@ -1764,6 +1787,16 @@ func autoConvert_v1_HTTPGetAction_To_api_HTTPGetAction(in *v1.HTTPGetAction, out
}
out.Host = in.Host
out.Scheme = api.URIScheme(in.Scheme)
if in.HTTPHeaders != nil {
out.HTTPHeaders = make([]api.HTTPHeader, len(in.HTTPHeaders))
for i := range in.HTTPHeaders {
if err := Convert_v1_HTTPHeader_To_api_HTTPHeader(&in.HTTPHeaders[i], &out.HTTPHeaders[i], s); err != nil {
return err
}
}
} else {
out.HTTPHeaders = nil
}
return nil
}
@ -1771,6 +1804,19 @@ func Convert_v1_HTTPGetAction_To_api_HTTPGetAction(in *v1.HTTPGetAction, out *ap
return autoConvert_v1_HTTPGetAction_To_api_HTTPGetAction(in, out, s)
}
func autoConvert_v1_HTTPHeader_To_api_HTTPHeader(in *v1.HTTPHeader, out *api.HTTPHeader, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*v1.HTTPHeader))(in)
}
out.Name = in.Name
out.Value = in.Value
return nil
}
func Convert_v1_HTTPHeader_To_api_HTTPHeader(in *v1.HTTPHeader, out *api.HTTPHeader, s conversion.Scope) error {
return autoConvert_v1_HTTPHeader_To_api_HTTPHeader(in, out, s)
}
func autoConvert_v1_Handler_To_api_Handler(in *v1.Handler, out *api.Handler, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*v1.Handler))(in)
@ -5132,6 +5178,7 @@ func init() {
autoConvert_api_GitRepoVolumeSource_To_v1_GitRepoVolumeSource,
autoConvert_api_GlusterfsVolumeSource_To_v1_GlusterfsVolumeSource,
autoConvert_api_HTTPGetAction_To_v1_HTTPGetAction,
autoConvert_api_HTTPHeader_To_v1_HTTPHeader,
autoConvert_api_Handler_To_v1_Handler,
autoConvert_api_HostPathVolumeSource_To_v1_HostPathVolumeSource,
autoConvert_api_ISCSIVolumeSource_To_v1_ISCSIVolumeSource,
@ -5237,6 +5284,7 @@ func init() {
autoConvert_v1_GitRepoVolumeSource_To_api_GitRepoVolumeSource,
autoConvert_v1_GlusterfsVolumeSource_To_api_GlusterfsVolumeSource,
autoConvert_v1_HTTPGetAction_To_api_HTTPGetAction,
autoConvert_v1_HTTPHeader_To_api_HTTPHeader,
autoConvert_v1_Handler_To_api_Handler,
autoConvert_v1_HostPathVolumeSource_To_api_HostPathVolumeSource,
autoConvert_v1_ISCSIVolumeSource_To_api_ISCSIVolumeSource,

View File

@ -392,6 +392,22 @@ func deepCopy_v1_HTTPGetAction(in v1.HTTPGetAction, out *v1.HTTPGetAction, c *co
}
out.Host = in.Host
out.Scheme = in.Scheme
if in.HTTPHeaders != nil {
out.HTTPHeaders = make([]v1.HTTPHeader, len(in.HTTPHeaders))
for i := range in.HTTPHeaders {
if err := deepCopy_v1_HTTPHeader(in.HTTPHeaders[i], &out.HTTPHeaders[i], c); err != nil {
return err
}
}
} else {
out.HTTPHeaders = nil
}
return nil
}
func deepCopy_v1_HTTPHeader(in v1.HTTPHeader, out *v1.HTTPHeader, c *conversion.Cloner) error {
out.Name = in.Name
out.Value = in.Value
return nil
}
@ -1967,6 +1983,7 @@ func init() {
deepCopy_v1_GitRepoVolumeSource,
deepCopy_v1_GlusterfsVolumeSource,
deepCopy_v1_HTTPGetAction,
deepCopy_v1_HTTPHeader,
deepCopy_v1_Handler,
deepCopy_v1_HostPathVolumeSource,
deepCopy_v1_ISCSIVolumeSource,

View File

@ -59,7 +59,7 @@ func (server *Server) DoServerCheck(prober httpprober.HTTPProber) (probe.Result,
}
url := utilnet.FormatURL(scheme, server.Addr, server.Port, server.Path)
result, data, err := prober.Probe(url, probeTimeOut)
result, data, err := prober.Probe(url, nil, probeTimeOut)
if err != nil {
return probe.Unknown, "", err

View File

@ -22,6 +22,7 @@ import (
"testing"
"k8s.io/kubernetes/pkg/probe"
"net/http"
"net/url"
"time"
)
@ -32,7 +33,7 @@ type fakeHttpProber struct {
err error
}
func (f *fakeHttpProber) Probe(*url.URL, time.Duration) (probe.Result, string, error) {
func (f *fakeHttpProber) Probe(*url.URL, http.Header, time.Duration) (probe.Result, string, error) {
return f.result, f.body, f.err
}

View File

@ -19,6 +19,7 @@ package prober
import (
"fmt"
"net"
"net/http"
"net/url"
"strconv"
"strings"
@ -126,6 +127,16 @@ func (pb *prober) runProbeWithRetries(p *api.Probe, pod *api.Pod, status api.Pod
return result, output, err
}
// buildHeaderMap takes a list of HTTPHeader <name, value> string
// pairs and returns a a populated string->[]string http.Header map.
func buildHeader(headerList []api.HTTPHeader) http.Header {
headers := make(http.Header)
for _, header := range headerList {
headers[header.Name] = append(headers[header.Name], header.Value)
}
return headers
}
func (pb *prober) runProbe(p *api.Probe, pod *api.Pod, status api.PodStatus, container api.Container, containerID kubecontainer.ContainerID) (probe.Result, string, error) {
timeout := time.Duration(p.TimeoutSeconds) * time.Second
if p.Exec != nil {
@ -145,7 +156,9 @@ func (pb *prober) runProbe(p *api.Probe, pod *api.Pod, status api.PodStatus, con
path := p.HTTPGet.Path
glog.V(4).Infof("HTTP-Probe Host: %v://%v, Port: %v, Path: %v", scheme, host, port, path)
url := formatURL(scheme, host, port, path)
return pb.http.Probe(url, timeout)
headers := buildHeader(p.HTTPGet.HTTPHeaders)
glog.V(4).Infof("HTTP-Probe Headers: %v", headers)
return pb.http.Probe(url, headers, timeout)
}
if p.TCPSocket != nil {
port, err := extractPort(p.TCPSocket.Port, container)

View File

@ -19,6 +19,8 @@ package prober
import (
"errors"
"fmt"
"net/http"
"reflect"
"testing"
"k8s.io/kubernetes/pkg/api"
@ -164,6 +166,33 @@ func TestGetTCPAddrParts(t *testing.T) {
}
}
func TestHTTPHeaders(t *testing.T) {
testCases := []struct {
input []api.HTTPHeader
output http.Header
}{
{[]api.HTTPHeader{}, http.Header{}},
{[]api.HTTPHeader{
{"X-Muffins-Or-Cupcakes", "Muffins"},
}, http.Header{"X-Muffins-Or-Cupcakes": {"Muffins"}}},
{[]api.HTTPHeader{
{"X-Muffins-Or-Cupcakes", "Muffins"},
{"X-Muffins-Or-Plumcakes", "Muffins!"},
}, http.Header{"X-Muffins-Or-Cupcakes": {"Muffins"},
"X-Muffins-Or-Plumcakes": {"Muffins!"}}},
{[]api.HTTPHeader{
{"X-Muffins-Or-Cupcakes", "Muffins"},
{"X-Muffins-Or-Cupcakes", "Cupcakes, too"},
}, http.Header{"X-Muffins-Or-Cupcakes": {"Muffins", "Cupcakes, too"}}},
}
for _, test := range testCases {
headers := buildHeader(test.input)
if !reflect.DeepEqual(test.output, headers) {
t.Errorf("Expected %#v, got %#v", test.output, headers)
}
}
}
func TestProbe(t *testing.T) {
prober := &prober{
refManager: kubecontainer.NewRefManager(),

View File

@ -36,7 +36,7 @@ func New() HTTPProber {
}
type HTTPProber interface {
Probe(url *url.URL, timeout time.Duration) (probe.Result, string, error)
Probe(url *url.URL, headers http.Header, timeout time.Duration) (probe.Result, string, error)
}
type httpProber struct {
@ -44,20 +44,26 @@ type httpProber struct {
}
// Probe returns a ProbeRunner capable of running an http check.
func (pr httpProber) Probe(url *url.URL, timeout time.Duration) (probe.Result, string, error) {
return DoHTTPProbe(url, &http.Client{Timeout: timeout, Transport: pr.transport})
func (pr httpProber) Probe(url *url.URL, headers http.Header, timeout time.Duration) (probe.Result, string, error) {
return DoHTTPProbe(url, headers, &http.Client{Timeout: timeout, Transport: pr.transport})
}
type HTTPGetInterface interface {
Get(u string) (*http.Response, error)
Do(req *http.Request) (*http.Response, error)
}
// DoHTTPProbe checks if a GET request to the url succeeds.
// If the HTTP response code is successful (i.e. 400 > code >= 200), it returns Success.
// If the HTTP response code is unsuccessful or HTTP communication fails, it returns Failure.
// This is exported because some other packages may want to do direct HTTP probes.
func DoHTTPProbe(url *url.URL, client HTTPGetInterface) (probe.Result, string, error) {
res, err := client.Get(url.String())
func DoHTTPProbe(url *url.URL, headers http.Header, client HTTPGetInterface) (probe.Result, string, error) {
req, err := http.NewRequest("GET", url.String(), nil)
if err != nil {
// Convert errors into failures to catch timeouts.
return probe.Failure, err.Error(), nil
}
req.Header = headers
res, err := client.Do(req)
if err != nil {
// Convert errors into failures to catch timeouts.
return probe.Failure, err.Error(), nil

View File

@ -42,8 +42,8 @@ func containsAny(s string, substrs []string) bool {
}
func TestHTTPProbeChecker(t *testing.T) {
handleReq := func(s int, body string) func(w http.ResponseWriter) {
return func(w http.ResponseWriter) {
handleReq := func(s int, body string) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(s)
w.Write([]byte(body))
}
@ -51,8 +51,9 @@ func TestHTTPProbeChecker(t *testing.T) {
prober := New()
testCases := []struct {
handler func(w http.ResponseWriter)
health probe.Result
handler func(w http.ResponseWriter, r *http.Request)
reqHeaders http.Header
health probe.Result
// go1.5: error message changed for timeout, need to support
// both old and new
accBodies []string
@ -60,18 +61,41 @@ func TestHTTPProbeChecker(t *testing.T) {
// The probe will be filled in below. This is primarily testing that an HTTP GET happens.
{
handleReq(http.StatusOK, "ok body"),
nil,
probe.Success,
[]string{"ok body"},
},
{
// Echo handler that returns the contents of request headers in the body
func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
output := ""
for k, arr := range r.Header {
for _, v := range arr {
output += fmt.Sprintf("%s: %s\n", k, v)
}
}
w.Write([]byte(output))
},
http.Header{
"X-Muffins-Or-Cupcakes": {"muffins"},
},
probe.Success,
[]string{
"X-Muffins-Or-Cupcakes: muffins",
},
},
{
handleReq(FailureCode, "fail body"),
nil,
probe.Failure,
[]string{fmt.Sprintf("HTTP probe failed with statuscode: %d", FailureCode)},
},
{
func(w http.ResponseWriter) {
func(w http.ResponseWriter, r *http.Request) {
time.Sleep(3 * time.Second)
},
nil,
probe.Failure,
[]string{
"use of closed network connection",
@ -82,7 +106,7 @@ func TestHTTPProbeChecker(t *testing.T) {
for _, test := range testCases {
// TODO: Close() this when fix #19254
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
test.handler(w)
test.handler(w, r)
}))
u, err := url.Parse(server.URL)
if err != nil {
@ -96,7 +120,7 @@ func TestHTTPProbeChecker(t *testing.T) {
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
health, output, err := prober.Probe(u, 1*time.Second)
health, output, err := prober.Probe(u, test.reqHeaders, 1*time.Second)
if test.health == probe.Unknown && err == nil {
t.Errorf("Expected error")
}

View File

@ -26,6 +26,7 @@ import (
"k8s.io/kubernetes/pkg/apiserver"
"k8s.io/kubernetes/pkg/probe"
"k8s.io/kubernetes/pkg/util"
"net/http"
"net/url"
"time"
)
@ -36,7 +37,7 @@ type fakeHttpProber struct {
err error
}
func (f *fakeHttpProber) Probe(*url.URL, time.Duration) (probe.Result, string, error) {
func (f *fakeHttpProber) Probe(*url.URL, http.Header, time.Duration) (probe.Result, string, error) {
return f.result, f.body, f.err
}

View File

@ -167,3 +167,13 @@ var percentRegexp = regexp.MustCompile("^" + percentFmt + "$")
func IsValidPercent(percent string) bool {
return percentRegexp.MatchString(percent)
}
const HTTPHeaderNameFmt string = "[-A-Za-z0-9]+"
var httpHeaderNameRegexp = regexp.MustCompile("^" + HTTPHeaderNameFmt + "$")
// IsHTTPHeaderName checks that a string conforms to the Go HTTP library's
// definition of a valid header field name (a stricter subset than RFC7230).
func IsHTTPHeaderName(value string) bool {
return httpHeaderNameRegexp.MatchString(value)
}

View File

@ -308,3 +308,30 @@ func TestIsValidIP(t *testing.T) {
}
}
}
func TestIsHTTPHeaderName(t *testing.T) {
goodValues := []string{
// Common ones
"Accept-Encoding", "Host", "If-Modified-Since", "X-Forwarded-For",
// Weirdo, but still conforming names
"a", "ab", "abc", "a1", "-a", "a-", "a-b", "a-1", "a--1--2--b", "--abc-123",
"A", "AB", "AbC", "A1", "-A", "A-", "A-B", "A-1", "A--1--2--B", "--123-ABC",
}
for _, val := range goodValues {
if !IsHTTPHeaderName(val) {
t.Errorf("expected true for '%s'", val)
}
}
badValues := []string{
"Host:", "X-Forwarded-For:", "X-@Home",
"", "_", "a_", "_a", "1_", "1_2", ".", "a.", ".a", "a.b", "1.", ".1", "1.2",
" ", "a ", " a", "a b", "1 ", " 1", "1 2", "#a#", "^", ",", ";", "=", "<",
"?", "@", "{",
}
for _, val := range badValues {
if IsHTTPHeaderName(val) {
t.Errorf("expected false for '%s'", val)
}
}
}