mirror of https://github.com/k3s-io/k3s
implement taints and tolerations
parent
952e8302fb
commit
52fb89ff73
|
@ -55,6 +55,7 @@ docs/man/man1/kubectl-run.1
|
|||
docs/man/man1/kubectl-scale.1
|
||||
docs/man/man1/kubectl-set.1
|
||||
docs/man/man1/kubectl-stop.1
|
||||
docs/man/man1/kubectl-taint.1
|
||||
docs/man/man1/kubectl-uncordon.1
|
||||
docs/man/man1/kubectl-version.1
|
||||
docs/man/man1/kubectl.1
|
||||
|
@ -108,6 +109,7 @@ docs/user-guide/kubectl/kubectl_rollout_undo.md
|
|||
docs/user-guide/kubectl/kubectl_run.md
|
||||
docs/user-guide/kubectl/kubectl_scale.md
|
||||
docs/user-guide/kubectl/kubectl_set.md
|
||||
docs/user-guide/kubectl/kubectl_taint.md
|
||||
docs/user-guide/kubectl/kubectl_uncordon.md
|
||||
docs/user-guide/kubectl/kubectl_version.md
|
||||
docs/yaml/kubectl/kubectl.yaml
|
||||
|
@ -142,5 +144,6 @@ docs/yaml/kubectl/kubectl_run.yaml
|
|||
docs/yaml/kubectl/kubectl_scale.yaml
|
||||
docs/yaml/kubectl/kubectl_set.yaml
|
||||
docs/yaml/kubectl/kubectl_stop.yaml
|
||||
docs/yaml/kubectl/kubectl_taint.yaml
|
||||
docs/yaml/kubectl/kubectl_uncordon.yaml
|
||||
docs/yaml/kubectl/kubectl_version.yaml
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
- [Other notable changes](#other-notable-changes)
|
||||
- [v1.3.0-alpha.3](#v130-alpha3)
|
||||
- [Downloads](#downloads)
|
||||
- [v1.3.0-alpha.3](#v130-alpha3)
|
||||
- [Changes since v1.3.0-alpha.2](#changes-since-v130-alpha2)
|
||||
- [Action Required](#action-required)
|
||||
- [Other notable changes](#other-notable-changes)
|
||||
|
|
|
@ -2955,6 +2955,96 @@ _kubectl_annotate()
|
|||
noun_aliases+=("svc")
|
||||
}
|
||||
|
||||
_kubectl_taint()
|
||||
{
|
||||
last_command="kubectl_taint"
|
||||
commands=()
|
||||
|
||||
flags=()
|
||||
two_word_flags=()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--all")
|
||||
flags+=("--include-extended-apis")
|
||||
flags+=("--no-headers")
|
||||
flags+=("--output=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--output-version=")
|
||||
flags+=("--overwrite")
|
||||
flags+=("--schema-cache-dir=")
|
||||
flags_with_completion+=("--schema-cache-dir")
|
||||
flags_completion+=("_filedir")
|
||||
flags+=("--selector=")
|
||||
two_word_flags+=("-l")
|
||||
flags+=("--show-all")
|
||||
flags+=("-a")
|
||||
flags+=("--show-labels")
|
||||
flags+=("--sort-by=")
|
||||
flags+=("--template=")
|
||||
flags_with_completion+=("--template")
|
||||
flags_completion+=("_filedir")
|
||||
flags+=("--validate")
|
||||
flags+=("--alsologtostderr")
|
||||
flags+=("--api-version=")
|
||||
flags+=("--as=")
|
||||
flags+=("--certificate-authority=")
|
||||
flags+=("--client-certificate=")
|
||||
flags+=("--client-key=")
|
||||
flags+=("--cluster=")
|
||||
flags+=("--context=")
|
||||
flags+=("--insecure-skip-tls-verify")
|
||||
flags+=("--kubeconfig=")
|
||||
flags+=("--log-backtrace-at=")
|
||||
flags+=("--log-dir=")
|
||||
flags+=("--log-flush-frequency=")
|
||||
flags+=("--logtostderr")
|
||||
flags+=("--match-server-version")
|
||||
flags+=("--namespace=")
|
||||
flags_with_completion+=("--namespace")
|
||||
flags_completion+=("__kubectl_get_namespaces")
|
||||
flags+=("--password=")
|
||||
flags+=("--server=")
|
||||
two_word_flags+=("-s")
|
||||
flags+=("--stderrthreshold=")
|
||||
flags+=("--token=")
|
||||
flags+=("--user=")
|
||||
flags+=("--username=")
|
||||
flags+=("--v=")
|
||||
flags+=("--vmodule=")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
must_have_one_noun+=("cluster")
|
||||
must_have_one_noun+=("componentstatus")
|
||||
must_have_one_noun+=("configmap")
|
||||
must_have_one_noun+=("daemonset")
|
||||
must_have_one_noun+=("deployment")
|
||||
must_have_one_noun+=("endpoints")
|
||||
must_have_one_noun+=("event")
|
||||
must_have_one_noun+=("horizontalpodautoscaler")
|
||||
must_have_one_noun+=("ingress")
|
||||
must_have_one_noun+=("job")
|
||||
must_have_one_noun+=("limitrange")
|
||||
must_have_one_noun+=("namespace")
|
||||
must_have_one_noun+=("node")
|
||||
must_have_one_noun+=("persistentvolume")
|
||||
must_have_one_noun+=("persistentvolumeclaim")
|
||||
must_have_one_noun+=("petset")
|
||||
must_have_one_noun+=("pod")
|
||||
must_have_one_noun+=("podsecuritypolicy")
|
||||
must_have_one_noun+=("podtemplate")
|
||||
must_have_one_noun+=("replicaset")
|
||||
must_have_one_noun+=("replicationcontroller")
|
||||
must_have_one_noun+=("resourcequota")
|
||||
must_have_one_noun+=("secret")
|
||||
must_have_one_noun+=("service")
|
||||
must_have_one_noun+=("serviceaccount")
|
||||
must_have_one_noun+=("thirdpartyresource")
|
||||
must_have_one_noun+=("thirdpartyresourcedata")
|
||||
noun_aliases=()
|
||||
}
|
||||
|
||||
_kubectl_config_view()
|
||||
{
|
||||
last_command="kubectl_config_view"
|
||||
|
@ -3648,6 +3738,7 @@ _kubectl()
|
|||
commands+=("rollout")
|
||||
commands+=("label")
|
||||
commands+=("annotate")
|
||||
commands+=("taint")
|
||||
commands+=("config")
|
||||
commands+=("cluster-info")
|
||||
commands+=("api-versions")
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
.TH "KUBERNETES" "1" " kubernetes User Manuals" "Eric Paris" "Jan 2015" ""
|
||||
|
||||
|
||||
.SH NAME
|
||||
.PP
|
||||
kubectl taint \- Update the taints on one or more nodes
|
||||
|
||||
|
||||
.SH SYNOPSIS
|
||||
.PP
|
||||
\fBkubectl taint\fP [OPTIONS]
|
||||
|
||||
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
Update the taints on one or more nodes.
|
||||
|
||||
.PP
|
||||
A taint consists of a key, value, and effect. As an argument here, it is expressed as key=value:effect.
|
||||
The key must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to 253 characters.
|
||||
The value must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to 253 characters.
|
||||
The effect must be NoSchedule or PreferNoSchedule.
|
||||
Currently taint can only apply to node.
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
.PP
|
||||
\fB\-\-all\fP=false
|
||||
select all nodes in the cluster
|
||||
|
||||
.PP
|
||||
\fB\-\-include\-extended\-apis\fP=true
|
||||
If true, include definitions of new APIs via calls to the API server. [default true]
|
||||
|
||||
.PP
|
||||
\fB\-\-no\-headers\fP=false
|
||||
When using the default output, don't print headers.
|
||||
|
||||
.PP
|
||||
\fB\-o\fP, \fB\-\-output\fP=""
|
||||
Output format. One of: json|yaml|wide|name|go\-template=...|go\-template\-file=...|jsonpath=...|jsonpath\-file=... See golang template [
|
||||
\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]] and jsonpath template [
|
||||
\[la]http://releases.k8s.io/HEAD/docs/user-guide/jsonpath.md\[ra]].
|
||||
|
||||
.PP
|
||||
\fB\-\-output\-version\fP=""
|
||||
Output the formatted object with the given group version (for ex: 'extensions/v1beta1').
|
||||
|
||||
.PP
|
||||
\fB\-\-overwrite\fP=false
|
||||
If true, allow taints to be overwritten, otherwise reject taint updates that overwrite existing taints.
|
||||
|
||||
.PP
|
||||
\fB\-\-schema\-cache\-dir\fP="\~/.kube/schema"
|
||||
If non\-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema'
|
||||
|
||||
.PP
|
||||
\fB\-l\fP, \fB\-\-selector\fP=""
|
||||
Selector (label query) to filter on
|
||||
|
||||
.PP
|
||||
\fB\-a\fP, \fB\-\-show\-all\fP=false
|
||||
When printing, show all resources (default hide terminated pods.)
|
||||
|
||||
.PP
|
||||
\fB\-\-show\-labels\fP=false
|
||||
When printing, show all labels as the last column (default hide labels column)
|
||||
|
||||
.PP
|
||||
\fB\-\-sort\-by\fP=""
|
||||
If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. '{.metadata.name}'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
|
||||
|
||||
.PP
|
||||
\fB\-\-template\fP=""
|
||||
Template string or path to template file to use when \-o=go\-template, \-o=go\-template\-file. The template format is golang templates [
|
||||
\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]].
|
||||
|
||||
.PP
|
||||
\fB\-\-validate\fP=true
|
||||
If true, use a schema to validate the input before sending it
|
||||
|
||||
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-alsologtostderr\fP=false
|
||||
log to standard error as well as files
|
||||
|
||||
.PP
|
||||
\fB\-\-api\-version\fP=""
|
||||
DEPRECATED: The API version to use when talking to the server
|
||||
|
||||
.PP
|
||||
\fB\-\-as\fP=""
|
||||
Username to impersonate for the operation.
|
||||
|
||||
.PP
|
||||
\fB\-\-certificate\-authority\fP=""
|
||||
Path to a cert. file for the certificate authority.
|
||||
|
||||
.PP
|
||||
\fB\-\-client\-certificate\fP=""
|
||||
Path to a client certificate file for TLS.
|
||||
|
||||
.PP
|
||||
\fB\-\-client\-key\fP=""
|
||||
Path to a client key file for TLS.
|
||||
|
||||
.PP
|
||||
\fB\-\-cluster\fP=""
|
||||
The name of the kubeconfig cluster to use
|
||||
|
||||
.PP
|
||||
\fB\-\-context\fP=""
|
||||
The name of the kubeconfig context to use
|
||||
|
||||
.PP
|
||||
\fB\-\-insecure\-skip\-tls\-verify\fP=false
|
||||
If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.
|
||||
|
||||
.PP
|
||||
\fB\-\-kubeconfig\fP=""
|
||||
Path to the kubeconfig file to use for CLI requests.
|
||||
|
||||
.PP
|
||||
\fB\-\-log\-backtrace\-at\fP=:0
|
||||
when logging hits line file:N, emit a stack trace
|
||||
|
||||
.PP
|
||||
\fB\-\-log\-dir\fP=""
|
||||
If non\-empty, write log files in this directory
|
||||
|
||||
.PP
|
||||
\fB\-\-log\-flush\-frequency\fP=5s
|
||||
Maximum number of seconds between log flushes
|
||||
|
||||
.PP
|
||||
\fB\-\-logtostderr\fP=true
|
||||
log to standard error instead of files
|
||||
|
||||
.PP
|
||||
\fB\-\-match\-server\-version\fP=false
|
||||
Require server version to match client version
|
||||
|
||||
.PP
|
||||
\fB\-\-namespace\fP=""
|
||||
If present, the namespace scope for this CLI request.
|
||||
|
||||
.PP
|
||||
\fB\-\-password\fP=""
|
||||
Password for basic authentication to the API server.
|
||||
|
||||
.PP
|
||||
\fB\-s\fP, \fB\-\-server\fP=""
|
||||
The address and port of the Kubernetes API server
|
||||
|
||||
.PP
|
||||
\fB\-\-stderrthreshold\fP=2
|
||||
logs at or above this threshold go to stderr
|
||||
|
||||
.PP
|
||||
\fB\-\-token\fP=""
|
||||
Bearer token for authentication to the API server.
|
||||
|
||||
.PP
|
||||
\fB\-\-user\fP=""
|
||||
The name of the kubeconfig user to use
|
||||
|
||||
.PP
|
||||
\fB\-\-username\fP=""
|
||||
Username for basic authentication to the API server.
|
||||
|
||||
.PP
|
||||
\fB\-\-v\fP=0
|
||||
log level for V logs
|
||||
|
||||
.PP
|
||||
\fB\-\-vmodule\fP=
|
||||
comma\-separated list of pattern=N settings for file\-filtered logging
|
||||
|
||||
|
||||
.SH EXAMPLE
|
||||
.PP
|
||||
.RS
|
||||
|
||||
.nf
|
||||
# Update node 'foo' with a taint with key 'dedicated' and value 'special\-user' and effect 'NoSchedule'.
|
||||
# If a taint with that key already exists, its value and effect are replaced as specified.
|
||||
kubectl taint nodes foo dedicated=special\-user:NoSchedule
|
||||
# Remove from node 'foo' the taint with key 'dedicated' if one exists.
|
||||
kubectl taint nodes foo dedicated\-
|
||||
|
||||
.fi
|
||||
.RE
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
\fBkubectl(1)\fP,
|
||||
|
||||
|
||||
.SH HISTORY
|
||||
.PP
|
||||
January 2015, Originally compiled by Eric Paris (eparis at redhat dot com) based on the kubernetes source material, but hopefully they have been automatically generated since!
|
|
@ -120,7 +120,7 @@ Find more information at
|
|||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
\fBkubectl\-get(1)\fP, \fBkubectl\-set(1)\fP, \fBkubectl\-describe(1)\fP, \fBkubectl\-create(1)\fP, \fBkubectl\-replace(1)\fP, \fBkubectl\-patch(1)\fP, \fBkubectl\-delete(1)\fP, \fBkubectl\-edit(1)\fP, \fBkubectl\-apply(1)\fP, \fBkubectl\-namespace(1)\fP, \fBkubectl\-logs(1)\fP, \fBkubectl\-rolling\-update(1)\fP, \fBkubectl\-scale(1)\fP, \fBkubectl\-cordon(1)\fP, \fBkubectl\-drain(1)\fP, \fBkubectl\-uncordon(1)\fP, \fBkubectl\-attach(1)\fP, \fBkubectl\-exec(1)\fP, \fBkubectl\-port\-forward(1)\fP, \fBkubectl\-proxy(1)\fP, \fBkubectl\-run(1)\fP, \fBkubectl\-stop(1)\fP, \fBkubectl\-expose(1)\fP, \fBkubectl\-autoscale(1)\fP, \fBkubectl\-rollout(1)\fP, \fBkubectl\-label(1)\fP, \fBkubectl\-annotate(1)\fP, \fBkubectl\-config(1)\fP, \fBkubectl\-cluster\-info(1)\fP, \fBkubectl\-api\-versions(1)\fP, \fBkubectl\-version(1)\fP, \fBkubectl\-explain(1)\fP, \fBkubectl\-convert(1)\fP,
|
||||
\fBkubectl\-get(1)\fP, \fBkubectl\-set(1)\fP, \fBkubectl\-describe(1)\fP, \fBkubectl\-create(1)\fP, \fBkubectl\-replace(1)\fP, \fBkubectl\-patch(1)\fP, \fBkubectl\-delete(1)\fP, \fBkubectl\-edit(1)\fP, \fBkubectl\-apply(1)\fP, \fBkubectl\-namespace(1)\fP, \fBkubectl\-logs(1)\fP, \fBkubectl\-rolling\-update(1)\fP, \fBkubectl\-scale(1)\fP, \fBkubectl\-cordon(1)\fP, \fBkubectl\-drain(1)\fP, \fBkubectl\-uncordon(1)\fP, \fBkubectl\-attach(1)\fP, \fBkubectl\-exec(1)\fP, \fBkubectl\-port\-forward(1)\fP, \fBkubectl\-proxy(1)\fP, \fBkubectl\-run(1)\fP, \fBkubectl\-stop(1)\fP, \fBkubectl\-expose(1)\fP, \fBkubectl\-autoscale(1)\fP, \fBkubectl\-rollout(1)\fP, \fBkubectl\-label(1)\fP, \fBkubectl\-annotate(1)\fP, \fBkubectl\-taint(1)\fP, \fBkubectl\-config(1)\fP, \fBkubectl\-cluster\-info(1)\fP, \fBkubectl\-api\-versions(1)\fP, \fBkubectl\-version(1)\fP, \fBkubectl\-explain(1)\fP, \fBkubectl\-convert(1)\fP,
|
||||
|
||||
|
||||
.SH HISTORY
|
||||
|
|
|
@ -107,10 +107,11 @@ kubectl
|
|||
* [kubectl run](kubectl_run.md) - Run a particular image on the cluster.
|
||||
* [kubectl scale](kubectl_scale.md) - Set a new size for a Deployment, ReplicaSet, Replication Controller, or Job.
|
||||
* [kubectl set](kubectl_set.md) - Set specific features on objects
|
||||
* [kubectl taint](kubectl_taint.md) - Update the taints on one or more nodes
|
||||
* [kubectl uncordon](kubectl_uncordon.md) - Mark node as schedulable
|
||||
* [kubectl version](kubectl_version.md) - Print the client and server version information.
|
||||
|
||||
###### Auto generated by spf13/cobra on 10-May-2016
|
||||
###### Auto generated by spf13/cobra on 15-May-2016
|
||||
|
||||
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
||||
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl.md?pixel)]()
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
<!-- BEGIN MUNGE: UNVERSIONED_WARNING -->
|
||||
|
||||
<!-- BEGIN STRIP_FOR_RELEASE -->
|
||||
|
||||
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
|
||||
width="25" height="25">
|
||||
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
|
||||
width="25" height="25">
|
||||
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
|
||||
width="25" height="25">
|
||||
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
|
||||
width="25" height="25">
|
||||
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
|
||||
width="25" height="25">
|
||||
|
||||
<h2>PLEASE NOTE: This document applies to the HEAD of the source tree</h2>
|
||||
|
||||
If you are using a released version of Kubernetes, you should
|
||||
refer to the docs that go with that version.
|
||||
|
||||
Documentation for other releases can be found at
|
||||
[releases.k8s.io](http://releases.k8s.io).
|
||||
</strong>
|
||||
--
|
||||
|
||||
<!-- END STRIP_FOR_RELEASE -->
|
||||
|
||||
<!-- END MUNGE: UNVERSIONED_WARNING -->
|
||||
|
||||
## kubectl taint
|
||||
|
||||
Update the taints on one or more nodes
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Update the taints on one or more nodes.
|
||||
|
||||
A taint consists of a key, value, and effect. As an argument here, it is expressed as key=value:effect.
|
||||
The key must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to 253 characters.
|
||||
The value must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to 253 characters.
|
||||
The effect must be NoSchedule or PreferNoSchedule.
|
||||
Currently taint can only apply to node.
|
||||
|
||||
```
|
||||
kubectl taint NODE NAME KEY_1=VAL_1:TAINT_EFFECT_1 ... KEY_N=VAL_N:TAINT_EFFECT_N
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Update node 'foo' with a taint with key 'dedicated' and value 'special-user' and effect 'NoSchedule'.
|
||||
# If a taint with that key already exists, its value and effect are replaced as specified.
|
||||
kubectl taint nodes foo dedicated=special-user:NoSchedule
|
||||
# Remove from node 'foo' the taint with key 'dedicated' if one exists.
|
||||
kubectl taint nodes foo dedicated-
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
--all[=false]: select all nodes in the cluster
|
||||
--include-extended-apis[=true]: If true, include definitions of new APIs via calls to the API server. [default true]
|
||||
--no-headers[=false]: When using the default output, don't print headers.
|
||||
-o, --output="": Output format. One of: json|yaml|wide|name|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://releases.k8s.io/HEAD/docs/user-guide/jsonpath.md].
|
||||
--output-version="": Output the formatted object with the given group version (for ex: 'extensions/v1beta1').
|
||||
--overwrite[=false]: If true, allow taints to be overwritten, otherwise reject taint updates that overwrite existing taints.
|
||||
--schema-cache-dir="~/.kube/schema": If non-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema'
|
||||
-l, --selector="": Selector (label query) to filter on
|
||||
-a, --show-all[=false]: When printing, show all resources (default hide terminated pods.)
|
||||
--show-labels[=false]: When printing, show all labels as the last column (default hide labels column)
|
||||
--sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. '{.metadata.name}'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
|
||||
--template="": Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].
|
||||
--validate[=true]: If true, use a schema to validate the input before sending it
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--alsologtostderr[=false]: log to standard error as well as files
|
||||
--as="": Username to impersonate for the operation.
|
||||
--certificate-authority="": Path to a cert. file for the certificate authority.
|
||||
--client-certificate="": Path to a client certificate file for TLS.
|
||||
--client-key="": Path to a client key file for TLS.
|
||||
--cluster="": The name of the kubeconfig cluster to use
|
||||
--context="": The name of the kubeconfig context to use
|
||||
--insecure-skip-tls-verify[=false]: If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.
|
||||
--kubeconfig="": Path to the kubeconfig file to use for CLI requests.
|
||||
--log-backtrace-at=:0: when logging hits line file:N, emit a stack trace
|
||||
--log-dir="": If non-empty, write log files in this directory
|
||||
--log-flush-frequency=5s: Maximum number of seconds between log flushes
|
||||
--logtostderr[=true]: log to standard error instead of files
|
||||
--match-server-version[=false]: Require server version to match client version
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--password="": Password for basic authentication to the API server.
|
||||
-s, --server="": The address and port of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
--token="": Bearer token for authentication to the API server.
|
||||
--user="": The name of the kubeconfig user to use
|
||||
--username="": Username for basic authentication to the API server.
|
||||
--v=0: log level for V logs
|
||||
--vmodule=: comma-separated list of pattern=N settings for file-filtered logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2016
|
||||
|
||||
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
||||
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_taint.md?pixel)]()
|
||||
<!-- END MUNGE: GENERATED_ANALYTICS -->
|
|
@ -93,6 +93,7 @@ see_also:
|
|||
- rollout
|
||||
- label
|
||||
- annotate
|
||||
- taint
|
||||
- config
|
||||
- cluster-info
|
||||
- api-versions
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
name: taint
|
||||
synopsis: Update the taints on one or more nodes
|
||||
description: |-
|
||||
Update the taints on one or more nodes.
|
||||
|
||||
A taint consists of a key, value, and effect. As an argument here, it is expressed as key=value:effect.
|
||||
The key must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to 253 characters.
|
||||
The value must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to 253 characters.
|
||||
The effect must be NoSchedule or PreferNoSchedule.
|
||||
Currently taint can only apply to node.
|
||||
options:
|
||||
- name: all
|
||||
default_value: "false"
|
||||
usage: select all nodes in the cluster
|
||||
- name: include-extended-apis
|
||||
default_value: "true"
|
||||
usage: |
|
||||
If true, include definitions of new APIs via calls to the API server. [default true]
|
||||
- name: no-headers
|
||||
default_value: "false"
|
||||
usage: When using the default output, don't print headers.
|
||||
- name: output
|
||||
shorthand: o
|
||||
usage: |
|
||||
Output format. One of: json|yaml|wide|name|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://releases.k8s.io/HEAD/docs/user-guide/jsonpath.md].
|
||||
- name: output-version
|
||||
usage: |
|
||||
Output the formatted object with the given group version (for ex: 'extensions/v1beta1').
|
||||
- name: overwrite
|
||||
default_value: "false"
|
||||
usage: |
|
||||
If true, allow taints to be overwritten, otherwise reject taint updates that overwrite existing taints.
|
||||
- name: schema-cache-dir
|
||||
default_value: ~/.kube/schema
|
||||
usage: |
|
||||
If non-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema'
|
||||
- name: selector
|
||||
shorthand: l
|
||||
usage: Selector (label query) to filter on
|
||||
- name: show-all
|
||||
shorthand: a
|
||||
default_value: "false"
|
||||
usage: |
|
||||
When printing, show all resources (default hide terminated pods.)
|
||||
- name: show-labels
|
||||
default_value: "false"
|
||||
usage: |
|
||||
When printing, show all labels as the last column (default hide labels column)
|
||||
- name: sort-by
|
||||
usage: |
|
||||
If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. '{.metadata.name}'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
|
||||
- name: template
|
||||
usage: |
|
||||
Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].
|
||||
- name: validate
|
||||
default_value: "true"
|
||||
usage: |
|
||||
If true, use a schema to validate the input before sending it
|
||||
inherited_options:
|
||||
- name: alsologtostderr
|
||||
default_value: "false"
|
||||
usage: log to standard error as well as files
|
||||
- name: api-version
|
||||
usage: |
|
||||
DEPRECATED: The API version to use when talking to the server
|
||||
- name: as
|
||||
usage: Username to impersonate for the operation.
|
||||
- name: certificate-authority
|
||||
usage: Path to a cert. file for the certificate authority.
|
||||
- name: client-certificate
|
||||
usage: Path to a client certificate file for TLS.
|
||||
- name: client-key
|
||||
usage: Path to a client key file for TLS.
|
||||
- name: cluster
|
||||
usage: The name of the kubeconfig cluster to use
|
||||
- name: context
|
||||
usage: The name of the kubeconfig context to use
|
||||
- name: insecure-skip-tls-verify
|
||||
default_value: "false"
|
||||
usage: |
|
||||
If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.
|
||||
- name: kubeconfig
|
||||
usage: Path to the kubeconfig file to use for CLI requests.
|
||||
- name: log-backtrace-at
|
||||
default_value: :0
|
||||
usage: when logging hits line file:N, emit a stack trace
|
||||
- name: log-dir
|
||||
usage: If non-empty, write log files in this directory
|
||||
- name: log-flush-frequency
|
||||
default_value: 5s
|
||||
usage: Maximum number of seconds between log flushes
|
||||
- name: logtostderr
|
||||
default_value: "true"
|
||||
usage: log to standard error instead of files
|
||||
- name: match-server-version
|
||||
default_value: "false"
|
||||
usage: Require server version to match client version
|
||||
- name: namespace
|
||||
usage: If present, the namespace scope for this CLI request.
|
||||
- name: password
|
||||
usage: Password for basic authentication to the API server.
|
||||
- name: server
|
||||
shorthand: s
|
||||
usage: The address and port of the Kubernetes API server
|
||||
- name: stderrthreshold
|
||||
default_value: "2"
|
||||
usage: logs at or above this threshold go to stderr
|
||||
- name: token
|
||||
usage: Bearer token for authentication to the API server.
|
||||
- name: user
|
||||
usage: The name of the kubeconfig user to use
|
||||
- name: username
|
||||
usage: Username for basic authentication to the API server.
|
||||
- name: v
|
||||
default_value: "0"
|
||||
usage: log level for V logs
|
||||
- name: vmodule
|
||||
usage: |
|
||||
comma-separated list of pattern=N settings for file-filtered logging
|
||||
example: |-
|
||||
# Update node 'foo' with a taint with key 'dedicated' and value 'special-user' and effect 'NoSchedule'.
|
||||
# If a taint with that key already exists, its value and effect are replaced as specified.
|
||||
kubectl taint nodes foo dedicated=special-user:NoSchedule
|
||||
# Remove from node 'foo' the taint with key 'dedicated' if one exists.
|
||||
kubectl taint nodes foo dedicated-
|
||||
see_also:
|
||||
- kubectl
|
|
@ -175,6 +175,8 @@ func init() {
|
|||
DeepCopy_api_ServiceSpec,
|
||||
DeepCopy_api_ServiceStatus,
|
||||
DeepCopy_api_TCPSocketAction,
|
||||
DeepCopy_api_Taint,
|
||||
DeepCopy_api_Toleration,
|
||||
DeepCopy_api_Volume,
|
||||
DeepCopy_api_VolumeMount,
|
||||
DeepCopy_api_VolumeSource,
|
||||
|
@ -2967,6 +2969,21 @@ func DeepCopy_api_TCPSocketAction(in TCPSocketAction, out *TCPSocketAction, c *c
|
|||
return nil
|
||||
}
|
||||
|
||||
func DeepCopy_api_Taint(in Taint, out *Taint, c *conversion.Cloner) error {
|
||||
out.Key = in.Key
|
||||
out.Value = in.Value
|
||||
out.Effect = in.Effect
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeepCopy_api_Toleration(in Toleration, out *Toleration, c *conversion.Cloner) error {
|
||||
out.Key = in.Key
|
||||
out.Operator = in.Operator
|
||||
out.Value = in.Value
|
||||
out.Effect = in.Effect
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeepCopy_api_Volume(in Volume, out *Volume, c *conversion.Cloner) error {
|
||||
out.Name = in.Name
|
||||
if err := DeepCopy_api_VolumeSource(in.VolumeSource, &out.VolumeSource, c); err != nil {
|
||||
|
|
|
@ -411,9 +411,19 @@ func NodeSelectorRequirementsAsSelector(nsm []NodeSelectorRequirement) (labels.S
|
|||
return selector, nil
|
||||
}
|
||||
|
||||
// AffinityAnnotationKey represents the key of affinity data (json serialized)
|
||||
// in the Annotations of a Pod.
|
||||
const AffinityAnnotationKey string = "scheduler.alpha.kubernetes.io/affinity"
|
||||
const (
|
||||
// AffinityAnnotationKey represents the key of affinity data (json serialized)
|
||||
// in the Annotations of a Pod.
|
||||
AffinityAnnotationKey string = "scheduler.alpha.kubernetes.io/affinity"
|
||||
|
||||
// TolerationsAnnotationKey represents the key of tolerations data (json serialized)
|
||||
// in the Annotations of a Pod.
|
||||
TolerationsAnnotationKey string = "scheduler.alpha.kubernetes.io/tolerations"
|
||||
|
||||
// TaintsAnnotationKey represents the key of taints data (json serialized)
|
||||
// in the Annotations of a Node.
|
||||
TaintsAnnotationKey string = "scheduler.alpha.kubernetes.io/taints"
|
||||
)
|
||||
|
||||
// GetAffinityFromPod gets the json serialized affinity data from Pod.Annotations
|
||||
// and converts it to the Affinity type in api.
|
||||
|
@ -427,3 +437,61 @@ func GetAffinityFromPodAnnotations(annotations map[string]string) (Affinity, err
|
|||
}
|
||||
return affinity, nil
|
||||
}
|
||||
|
||||
// GetTolerationsFromPodAnnotations gets the json serialized tolerations data from Pod.Annotations
|
||||
// and converts it to the []Toleration type in api.
|
||||
func GetTolerationsFromPodAnnotations(annotations map[string]string) ([]Toleration, error) {
|
||||
var tolerations []Toleration
|
||||
if len(annotations) > 0 && annotations[TolerationsAnnotationKey] != "" {
|
||||
err := json.Unmarshal([]byte(annotations[TolerationsAnnotationKey]), &tolerations)
|
||||
if err != nil {
|
||||
return tolerations, err
|
||||
}
|
||||
}
|
||||
return tolerations, nil
|
||||
}
|
||||
|
||||
// GetTaintsFromNodeAnnotations gets the json serialized taints data from Pod.Annotations
|
||||
// and converts it to the []Taint type in api.
|
||||
func GetTaintsFromNodeAnnotations(annotations map[string]string) ([]Taint, error) {
|
||||
var taints []Taint
|
||||
if len(annotations) > 0 && annotations[TaintsAnnotationKey] != "" {
|
||||
err := json.Unmarshal([]byte(annotations[TaintsAnnotationKey]), &taints)
|
||||
if err != nil {
|
||||
return []Taint{}, err
|
||||
}
|
||||
}
|
||||
return taints, nil
|
||||
}
|
||||
|
||||
// TolerationToleratesTaint checks if the toleration tolerates the taint.
|
||||
func TolerationToleratesTaint(toleration Toleration, taint Taint) bool {
|
||||
if len(toleration.Effect) != 0 && toleration.Effect != taint.Effect {
|
||||
return false
|
||||
}
|
||||
|
||||
if toleration.Key != taint.Key {
|
||||
return false
|
||||
}
|
||||
// TODO: Use proper defaulting when Toleration becomes a field of PodSpec
|
||||
if (len(toleration.Operator) == 0 || toleration.Operator == TolerationOpEqual) && toleration.Value == taint.Value {
|
||||
return true
|
||||
}
|
||||
if toleration.Operator == TolerationOpExists {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
// TaintToleratedByTolerations checks if taint is tolerated by any of the tolerations.
|
||||
func TaintToleratedByTolerations(taint Taint, tolerations []Toleration) bool {
|
||||
tolerated := false
|
||||
for _, toleration := range tolerations {
|
||||
if TolerationToleratesTaint(toleration, taint) {
|
||||
tolerated = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return tolerated
|
||||
}
|
||||
|
|
|
@ -24521,6 +24521,592 @@ func (x *PreferredSchedulingTerm) codecDecodeSelfFromArray(l int, d *codec1978.D
|
|||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
}
|
||||
|
||||
func (x *Taint) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperEncoder(e)
|
||||
_, _, _ = h, z, r
|
||||
if x == nil {
|
||||
r.EncodeNil()
|
||||
} else {
|
||||
yym1 := z.EncBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.EncExt(x) {
|
||||
} else {
|
||||
yysep2 := !z.EncBinary()
|
||||
yy2arr2 := z.EncBasicHandle().StructToArray
|
||||
var yyq2 [3]bool
|
||||
_, _, _ = yysep2, yyq2, yy2arr2
|
||||
const yyr2 bool = false
|
||||
yyq2[1] = x.Value != ""
|
||||
var yynn2 int
|
||||
if yyr2 || yy2arr2 {
|
||||
r.EncodeArrayStart(3)
|
||||
} else {
|
||||
yynn2 = 2
|
||||
for _, b := range yyq2 {
|
||||
if b {
|
||||
yynn2++
|
||||
}
|
||||
}
|
||||
r.EncodeMapStart(yynn2)
|
||||
yynn2 = 0
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
yym4 := z.EncBinary()
|
||||
_ = yym4
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Key))
|
||||
}
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("key"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
yym5 := z.EncBinary()
|
||||
_ = yym5
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Key))
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if yyq2[1] {
|
||||
yym7 := z.EncBinary()
|
||||
_ = yym7
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Value))
|
||||
}
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[1] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("value"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
yym8 := z.EncBinary()
|
||||
_ = yym8
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Value))
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
x.Effect.CodecEncodeSelf(e)
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("effect"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
x.Effect.CodecEncodeSelf(e)
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Taint) CodecDecodeSelf(d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
yym1 := z.DecBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.DecExt(x) {
|
||||
} else {
|
||||
yyct2 := r.ContainerType()
|
||||
if yyct2 == codecSelferValueTypeMap1234 {
|
||||
yyl2 := r.ReadMapStart()
|
||||
if yyl2 == 0 {
|
||||
z.DecSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
} else {
|
||||
x.codecDecodeSelfFromMap(yyl2, d)
|
||||
}
|
||||
} else if yyct2 == codecSelferValueTypeArray1234 {
|
||||
yyl2 := r.ReadArrayStart()
|
||||
if yyl2 == 0 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
} else {
|
||||
x.codecDecodeSelfFromArray(yyl2, d)
|
||||
}
|
||||
} else {
|
||||
panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Taint) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yys3Slc = z.DecScratchBuffer() // default slice to decode into
|
||||
_ = yys3Slc
|
||||
var yyhl3 bool = l >= 0
|
||||
for yyj3 := 0; ; yyj3++ {
|
||||
if yyhl3 {
|
||||
if yyj3 >= l {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if r.CheckBreak() {
|
||||
break
|
||||
}
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerMapKey1234)
|
||||
yys3Slc = r.DecodeBytes(yys3Slc, true, true)
|
||||
yys3 := string(yys3Slc)
|
||||
z.DecSendContainerState(codecSelfer_containerMapValue1234)
|
||||
switch yys3 {
|
||||
case "key":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Key = ""
|
||||
} else {
|
||||
x.Key = string(r.DecodeString())
|
||||
}
|
||||
case "value":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Value = ""
|
||||
} else {
|
||||
x.Value = string(r.DecodeString())
|
||||
}
|
||||
case "effect":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Effect = ""
|
||||
} else {
|
||||
x.Effect = TaintEffect(r.DecodeString())
|
||||
}
|
||||
default:
|
||||
z.DecStructFieldNotFound(-1, yys3)
|
||||
} // end switch yys3
|
||||
} // end for yyj3
|
||||
z.DecSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
}
|
||||
|
||||
func (x *Taint) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yyj7 int
|
||||
var yyb7 bool
|
||||
var yyhl7 bool = l >= 0
|
||||
yyj7++
|
||||
if yyhl7 {
|
||||
yyb7 = yyj7 > l
|
||||
} else {
|
||||
yyb7 = r.CheckBreak()
|
||||
}
|
||||
if yyb7 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Key = ""
|
||||
} else {
|
||||
x.Key = string(r.DecodeString())
|
||||
}
|
||||
yyj7++
|
||||
if yyhl7 {
|
||||
yyb7 = yyj7 > l
|
||||
} else {
|
||||
yyb7 = r.CheckBreak()
|
||||
}
|
||||
if yyb7 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Value = ""
|
||||
} else {
|
||||
x.Value = string(r.DecodeString())
|
||||
}
|
||||
yyj7++
|
||||
if yyhl7 {
|
||||
yyb7 = yyj7 > l
|
||||
} else {
|
||||
yyb7 = r.CheckBreak()
|
||||
}
|
||||
if yyb7 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Effect = ""
|
||||
} else {
|
||||
x.Effect = TaintEffect(r.DecodeString())
|
||||
}
|
||||
for {
|
||||
yyj7++
|
||||
if yyhl7 {
|
||||
yyb7 = yyj7 > l
|
||||
} else {
|
||||
yyb7 = r.CheckBreak()
|
||||
}
|
||||
if yyb7 {
|
||||
break
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
z.DecStructFieldNotFound(yyj7-1, "")
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
}
|
||||
|
||||
func (x TaintEffect) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperEncoder(e)
|
||||
_, _, _ = h, z, r
|
||||
yym1 := z.EncBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.EncExt(x) {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x))
|
||||
}
|
||||
}
|
||||
|
||||
func (x *TaintEffect) CodecDecodeSelf(d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
yym1 := z.DecBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.DecExt(x) {
|
||||
} else {
|
||||
*((*string)(x)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Toleration) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperEncoder(e)
|
||||
_, _, _ = h, z, r
|
||||
if x == nil {
|
||||
r.EncodeNil()
|
||||
} else {
|
||||
yym1 := z.EncBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.EncExt(x) {
|
||||
} else {
|
||||
yysep2 := !z.EncBinary()
|
||||
yy2arr2 := z.EncBasicHandle().StructToArray
|
||||
var yyq2 [4]bool
|
||||
_, _, _ = yysep2, yyq2, yy2arr2
|
||||
const yyr2 bool = false
|
||||
yyq2[0] = x.Key != ""
|
||||
yyq2[1] = x.Operator != ""
|
||||
yyq2[2] = x.Value != ""
|
||||
yyq2[3] = x.Effect != ""
|
||||
var yynn2 int
|
||||
if yyr2 || yy2arr2 {
|
||||
r.EncodeArrayStart(4)
|
||||
} else {
|
||||
yynn2 = 0
|
||||
for _, b := range yyq2 {
|
||||
if b {
|
||||
yynn2++
|
||||
}
|
||||
}
|
||||
r.EncodeMapStart(yynn2)
|
||||
yynn2 = 0
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if yyq2[0] {
|
||||
yym4 := z.EncBinary()
|
||||
_ = yym4
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Key))
|
||||
}
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[0] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("key"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
yym5 := z.EncBinary()
|
||||
_ = yym5
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Key))
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if yyq2[1] {
|
||||
x.Operator.CodecEncodeSelf(e)
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[1] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("operator"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
x.Operator.CodecEncodeSelf(e)
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if yyq2[2] {
|
||||
yym10 := z.EncBinary()
|
||||
_ = yym10
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Value))
|
||||
}
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[2] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("value"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
yym11 := z.EncBinary()
|
||||
_ = yym11
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Value))
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if yyq2[3] {
|
||||
x.Effect.CodecEncodeSelf(e)
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[3] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("effect"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
x.Effect.CodecEncodeSelf(e)
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Toleration) CodecDecodeSelf(d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
yym1 := z.DecBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.DecExt(x) {
|
||||
} else {
|
||||
yyct2 := r.ContainerType()
|
||||
if yyct2 == codecSelferValueTypeMap1234 {
|
||||
yyl2 := r.ReadMapStart()
|
||||
if yyl2 == 0 {
|
||||
z.DecSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
} else {
|
||||
x.codecDecodeSelfFromMap(yyl2, d)
|
||||
}
|
||||
} else if yyct2 == codecSelferValueTypeArray1234 {
|
||||
yyl2 := r.ReadArrayStart()
|
||||
if yyl2 == 0 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
} else {
|
||||
x.codecDecodeSelfFromArray(yyl2, d)
|
||||
}
|
||||
} else {
|
||||
panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Toleration) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yys3Slc = z.DecScratchBuffer() // default slice to decode into
|
||||
_ = yys3Slc
|
||||
var yyhl3 bool = l >= 0
|
||||
for yyj3 := 0; ; yyj3++ {
|
||||
if yyhl3 {
|
||||
if yyj3 >= l {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if r.CheckBreak() {
|
||||
break
|
||||
}
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerMapKey1234)
|
||||
yys3Slc = r.DecodeBytes(yys3Slc, true, true)
|
||||
yys3 := string(yys3Slc)
|
||||
z.DecSendContainerState(codecSelfer_containerMapValue1234)
|
||||
switch yys3 {
|
||||
case "key":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Key = ""
|
||||
} else {
|
||||
x.Key = string(r.DecodeString())
|
||||
}
|
||||
case "operator":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Operator = ""
|
||||
} else {
|
||||
x.Operator = TolerationOperator(r.DecodeString())
|
||||
}
|
||||
case "value":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Value = ""
|
||||
} else {
|
||||
x.Value = string(r.DecodeString())
|
||||
}
|
||||
case "effect":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Effect = ""
|
||||
} else {
|
||||
x.Effect = TaintEffect(r.DecodeString())
|
||||
}
|
||||
default:
|
||||
z.DecStructFieldNotFound(-1, yys3)
|
||||
} // end switch yys3
|
||||
} // end for yyj3
|
||||
z.DecSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
}
|
||||
|
||||
func (x *Toleration) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yyj8 int
|
||||
var yyb8 bool
|
||||
var yyhl8 bool = l >= 0
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb8 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Key = ""
|
||||
} else {
|
||||
x.Key = string(r.DecodeString())
|
||||
}
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb8 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Operator = ""
|
||||
} else {
|
||||
x.Operator = TolerationOperator(r.DecodeString())
|
||||
}
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb8 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Value = ""
|
||||
} else {
|
||||
x.Value = string(r.DecodeString())
|
||||
}
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb8 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Effect = ""
|
||||
} else {
|
||||
x.Effect = TaintEffect(r.DecodeString())
|
||||
}
|
||||
for {
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb8 {
|
||||
break
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
z.DecStructFieldNotFound(yyj8-1, "")
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
}
|
||||
|
||||
func (x TolerationOperator) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperEncoder(e)
|
||||
_, _, _ = h, z, r
|
||||
yym1 := z.EncBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.EncExt(x) {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x))
|
||||
}
|
||||
}
|
||||
|
||||
func (x *TolerationOperator) CodecDecodeSelf(d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
yym1 := z.DecBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.DecExt(x) {
|
||||
} else {
|
||||
*((*string)(x)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
|
||||
func (x *PodSpec) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperEncoder(e)
|
||||
|
|
|
@ -1308,6 +1308,73 @@ type PreferredSchedulingTerm struct {
|
|||
Preference NodeSelectorTerm `json:"preference"`
|
||||
}
|
||||
|
||||
// The node this Taint is attached to has the effect "effect" on
|
||||
// any pod that that does not tolerate the Taint.
|
||||
type Taint struct {
|
||||
// Required. The taint key to be applied to a node.
|
||||
Key string `json:"key" patchStrategy:"merge" patchMergeKey:"key"`
|
||||
// Required. The taint value corresponding to the taint key.
|
||||
Value string `json:"value,omitempty"`
|
||||
// Required. The effect of the taint on pods
|
||||
// that do not tolerate the taint.
|
||||
// Valid effects are NoSchedule and PreferNoSchedule.
|
||||
Effect TaintEffect `json:"effect"`
|
||||
}
|
||||
|
||||
type TaintEffect string
|
||||
|
||||
const (
|
||||
// Do not allow new pods to schedule onto the node unless they tolerate the taint,
|
||||
// but allow all pods submitted to Kubelet without going through the scheduler
|
||||
// to start, and allow all already-running pods to continue running.
|
||||
// Enforced by the scheduler.
|
||||
TaintEffectNoSchedule TaintEffect = "NoSchedule"
|
||||
// Like TaintEffectNoSchedule, but the scheduler tries not to schedule
|
||||
// new pods onto the node, rather than prohibiting new pods from scheduling
|
||||
// onto the node entirely. Enforced by the scheduler.
|
||||
TaintEffectPreferNoSchedule TaintEffect = "PreferNoSchedule"
|
||||
// NOT YET IMPLEMENTED. TODO: Uncomment field once it is implemented.
|
||||
// Do not allow new pods to schedule onto the node unless they tolerate the taint,
|
||||
// do not allow pods to start on Kubelet unless they tolerate the taint,
|
||||
// but allow all already-running pods to continue running.
|
||||
// Enforced by the scheduler and Kubelet.
|
||||
// TaintEffectNoScheduleNoAdmit TaintEffect = "NoScheduleNoAdmit"
|
||||
// NOT YET IMPLEMENTED. TODO: Uncomment field once it is implemented.
|
||||
// Do not allow new pods to schedule onto the node unless they tolerate the taint,
|
||||
// do not allow pods to start on Kubelet unless they tolerate the taint,
|
||||
// and evict any already-running pods that do not tolerate the taint.
|
||||
// Enforced by the scheduler and Kubelet.
|
||||
// TaintEffectNoScheduleNoAdmitNoExecute = "NoScheduleNoAdmitNoExecute"
|
||||
)
|
||||
|
||||
// The pod this Toleration is attached to tolerates any taint that matches
|
||||
// the triple <key,value,effect> using the matching operator <operator>.
|
||||
type Toleration struct {
|
||||
// Required. Key is the taint key that the toleration applies to.
|
||||
Key string `json:"key,omitempty" patchStrategy:"merge" patchMergeKey:"key"`
|
||||
// operator represents a key's relationship to the value.
|
||||
// Valid operators are Exists and Equal. Defaults to Equal.
|
||||
// Exists is equivalent to wildcard for value, so that a pod can
|
||||
// tolerate all taints of a particular category.
|
||||
Operator TolerationOperator `json:"operator,omitempty"`
|
||||
// Value is the taint value the toleration matches to.
|
||||
// If the operator is Exists, the value should be empty, otherwise just a regular string.
|
||||
Value string `json:"value,omitempty"`
|
||||
// Effect indicates the taint effect to match. Empty means match all taint effects.
|
||||
// When specified, allowed values are NoSchedule and PreferNoSchedule.
|
||||
Effect TaintEffect `json:"effect,omitempty"`
|
||||
// TODO: For forgiveness (#1574), we'd eventually add at least a grace period
|
||||
// here, and possibly an occurrence threshold and period.
|
||||
}
|
||||
|
||||
// A toleration operator is the set of operators that can be used in a toleration.
|
||||
type TolerationOperator string
|
||||
|
||||
const (
|
||||
TolerationOpExists TolerationOperator = "Exists"
|
||||
TolerationOpEqual TolerationOperator = "Equal"
|
||||
)
|
||||
|
||||
// PodSpec is a description of a pod
|
||||
type PodSpec struct {
|
||||
Volumes []Volume `json:"volumes"`
|
||||
|
|
|
@ -311,6 +311,10 @@ func init() {
|
|||
Convert_api_ServiceStatus_To_v1_ServiceStatus,
|
||||
Convert_v1_TCPSocketAction_To_api_TCPSocketAction,
|
||||
Convert_api_TCPSocketAction_To_v1_TCPSocketAction,
|
||||
Convert_v1_Taint_To_api_Taint,
|
||||
Convert_api_Taint_To_v1_Taint,
|
||||
Convert_v1_Toleration_To_api_Toleration,
|
||||
Convert_api_Toleration_To_v1_Toleration,
|
||||
Convert_v1_Volume_To_api_Volume,
|
||||
Convert_api_Volume_To_v1_Volume,
|
||||
Convert_v1_VolumeMount_To_api_VolumeMount,
|
||||
|
@ -6548,6 +6552,52 @@ func Convert_api_TCPSocketAction_To_v1_TCPSocketAction(in *api.TCPSocketAction,
|
|||
return autoConvert_api_TCPSocketAction_To_v1_TCPSocketAction(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_Taint_To_api_Taint(in *Taint, out *api.Taint, s conversion.Scope) error {
|
||||
out.Key = in.Key
|
||||
out.Value = in.Value
|
||||
out.Effect = api.TaintEffect(in.Effect)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_v1_Taint_To_api_Taint(in *Taint, out *api.Taint, s conversion.Scope) error {
|
||||
return autoConvert_v1_Taint_To_api_Taint(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_api_Taint_To_v1_Taint(in *api.Taint, out *Taint, s conversion.Scope) error {
|
||||
out.Key = in.Key
|
||||
out.Value = in.Value
|
||||
out.Effect = TaintEffect(in.Effect)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_api_Taint_To_v1_Taint(in *api.Taint, out *Taint, s conversion.Scope) error {
|
||||
return autoConvert_api_Taint_To_v1_Taint(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_Toleration_To_api_Toleration(in *Toleration, out *api.Toleration, s conversion.Scope) error {
|
||||
out.Key = in.Key
|
||||
out.Operator = api.TolerationOperator(in.Operator)
|
||||
out.Value = in.Value
|
||||
out.Effect = api.TaintEffect(in.Effect)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_v1_Toleration_To_api_Toleration(in *Toleration, out *api.Toleration, s conversion.Scope) error {
|
||||
return autoConvert_v1_Toleration_To_api_Toleration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_api_Toleration_To_v1_Toleration(in *api.Toleration, out *Toleration, s conversion.Scope) error {
|
||||
out.Key = in.Key
|
||||
out.Operator = TolerationOperator(in.Operator)
|
||||
out.Value = in.Value
|
||||
out.Effect = TaintEffect(in.Effect)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_api_Toleration_To_v1_Toleration(in *api.Toleration, out *Toleration, s conversion.Scope) error {
|
||||
return autoConvert_api_Toleration_To_v1_Toleration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_Volume_To_api_Volume(in *Volume, out *api.Volume, s conversion.Scope) error {
|
||||
SetDefaults_Volume(in)
|
||||
out.Name = in.Name
|
||||
|
|
|
@ -172,6 +172,8 @@ func init() {
|
|||
DeepCopy_v1_ServiceSpec,
|
||||
DeepCopy_v1_ServiceStatus,
|
||||
DeepCopy_v1_TCPSocketAction,
|
||||
DeepCopy_v1_Taint,
|
||||
DeepCopy_v1_Toleration,
|
||||
DeepCopy_v1_Volume,
|
||||
DeepCopy_v1_VolumeMount,
|
||||
DeepCopy_v1_VolumeSource,
|
||||
|
@ -2928,6 +2930,21 @@ func DeepCopy_v1_TCPSocketAction(in TCPSocketAction, out *TCPSocketAction, c *co
|
|||
return nil
|
||||
}
|
||||
|
||||
func DeepCopy_v1_Taint(in Taint, out *Taint, c *conversion.Cloner) error {
|
||||
out.Key = in.Key
|
||||
out.Value = in.Value
|
||||
out.Effect = in.Effect
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeepCopy_v1_Toleration(in Toleration, out *Toleration, c *conversion.Cloner) error {
|
||||
out.Key = in.Key
|
||||
out.Operator = in.Operator
|
||||
out.Value = in.Value
|
||||
out.Effect = in.Effect
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeepCopy_v1_Volume(in Volume, out *Volume, c *conversion.Cloner) error {
|
||||
out.Name = in.Name
|
||||
if err := DeepCopy_v1_VolumeSource(in.VolumeSource, &out.VolumeSource, c); err != nil {
|
||||
|
|
|
@ -165,6 +165,8 @@ limitations under the License.
|
|||
ServiceSpec
|
||||
ServiceStatus
|
||||
TCPSocketAction
|
||||
Taint
|
||||
Toleration
|
||||
Volume
|
||||
VolumeMount
|
||||
VolumeSource
|
||||
|
@ -749,6 +751,14 @@ func (m *TCPSocketAction) Reset() { *m = TCPSocketAction{} }
|
|||
func (m *TCPSocketAction) String() string { return proto.CompactTextString(m) }
|
||||
func (*TCPSocketAction) ProtoMessage() {}
|
||||
|
||||
func (m *Taint) Reset() { *m = Taint{} }
|
||||
func (m *Taint) String() string { return proto.CompactTextString(m) }
|
||||
func (*Taint) ProtoMessage() {}
|
||||
|
||||
func (m *Toleration) Reset() { *m = Toleration{} }
|
||||
func (m *Toleration) String() string { return proto.CompactTextString(m) }
|
||||
func (*Toleration) ProtoMessage() {}
|
||||
|
||||
func (m *Volume) Reset() { *m = Volume{} }
|
||||
func (m *Volume) String() string { return proto.CompactTextString(m) }
|
||||
func (*Volume) ProtoMessage() {}
|
||||
|
@ -906,6 +916,8 @@ func init() {
|
|||
proto.RegisterType((*ServiceSpec)(nil), "k8s.io.kubernetes.pkg.api.v1.ServiceSpec")
|
||||
proto.RegisterType((*ServiceStatus)(nil), "k8s.io.kubernetes.pkg.api.v1.ServiceStatus")
|
||||
proto.RegisterType((*TCPSocketAction)(nil), "k8s.io.kubernetes.pkg.api.v1.TCPSocketAction")
|
||||
proto.RegisterType((*Taint)(nil), "k8s.io.kubernetes.pkg.api.v1.Taint")
|
||||
proto.RegisterType((*Toleration)(nil), "k8s.io.kubernetes.pkg.api.v1.Toleration")
|
||||
proto.RegisterType((*Volume)(nil), "k8s.io.kubernetes.pkg.api.v1.Volume")
|
||||
proto.RegisterType((*VolumeMount)(nil), "k8s.io.kubernetes.pkg.api.v1.VolumeMount")
|
||||
proto.RegisterType((*VolumeSource)(nil), "k8s.io.kubernetes.pkg.api.v1.VolumeSource")
|
||||
|
@ -7111,6 +7123,70 @@ func (m *TCPSocketAction) MarshalTo(data []byte) (int, error) {
|
|||
return i, nil
|
||||
}
|
||||
|
||||
func (m *Taint) Marshal() (data []byte, err error) {
|
||||
size := m.Size()
|
||||
data = make([]byte, size)
|
||||
n, err := m.MarshalTo(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data[:n], nil
|
||||
}
|
||||
|
||||
func (m *Taint) MarshalTo(data []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
data[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintGenerated(data, i, uint64(len(m.Key)))
|
||||
i += copy(data[i:], m.Key)
|
||||
data[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintGenerated(data, i, uint64(len(m.Value)))
|
||||
i += copy(data[i:], m.Value)
|
||||
data[i] = 0x1a
|
||||
i++
|
||||
i = encodeVarintGenerated(data, i, uint64(len(m.Effect)))
|
||||
i += copy(data[i:], m.Effect)
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *Toleration) Marshal() (data []byte, err error) {
|
||||
size := m.Size()
|
||||
data = make([]byte, size)
|
||||
n, err := m.MarshalTo(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data[:n], nil
|
||||
}
|
||||
|
||||
func (m *Toleration) MarshalTo(data []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
data[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintGenerated(data, i, uint64(len(m.Key)))
|
||||
i += copy(data[i:], m.Key)
|
||||
data[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintGenerated(data, i, uint64(len(m.Operator)))
|
||||
i += copy(data[i:], m.Operator)
|
||||
data[i] = 0x1a
|
||||
i++
|
||||
i = encodeVarintGenerated(data, i, uint64(len(m.Value)))
|
||||
i += copy(data[i:], m.Value)
|
||||
data[i] = 0x22
|
||||
i++
|
||||
i = encodeVarintGenerated(data, i, uint64(len(m.Effect)))
|
||||
i += copy(data[i:], m.Effect)
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *Volume) Marshal() (data []byte, err error) {
|
||||
size := m.Size()
|
||||
data = make([]byte, size)
|
||||
|
@ -9724,6 +9800,32 @@ func (m *TCPSocketAction) Size() (n int) {
|
|||
return n
|
||||
}
|
||||
|
||||
func (m *Taint) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.Key)
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
l = len(m.Value)
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
l = len(m.Effect)
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *Toleration) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.Key)
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
l = len(m.Operator)
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
l = len(m.Value)
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
l = len(m.Effect)
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *Volume) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
|
@ -32142,6 +32244,309 @@ func (m *TCPSocketAction) Unmarshal(data []byte) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
func (m *Taint) Unmarshal(data []byte) error {
|
||||
l := len(data)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: Taint: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: Taint: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Key = string(data[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Value = string(data[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Effect", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Effect = TaintEffect(data[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipGenerated(data[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *Toleration) Unmarshal(data []byte) error {
|
||||
l := len(data)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: Toleration: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: Toleration: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Key = string(data[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Operator", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Operator = TolerationOperator(data[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Value = string(data[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Effect", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Effect = TaintEffect(data[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipGenerated(data[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *Volume) Unmarshal(data []byte) error {
|
||||
l := len(data)
|
||||
iNdEx := 0
|
||||
|
|
|
@ -2686,6 +2686,42 @@ message TCPSocketAction {
|
|||
optional k8s.io.kubernetes.pkg.util.intstr.IntOrString port = 1;
|
||||
}
|
||||
|
||||
// The node this Taint is attached to has the effect "effect" on
|
||||
// any pod that that does not tolerate the Taint.
|
||||
message Taint {
|
||||
// Required. The taint key to be applied to a node.
|
||||
optional string key = 1;
|
||||
|
||||
// Required. The taint value corresponding to the taint key.
|
||||
optional string value = 2;
|
||||
|
||||
// Required. The effect of the taint on pods
|
||||
// that do not tolerate the taint.
|
||||
// Valid effects are NoSchedule and PreferNoSchedule.
|
||||
optional string effect = 3;
|
||||
}
|
||||
|
||||
// The pod this Toleration is attached to tolerates any taint that matches
|
||||
// the triple <key,value,effect> using the matching operator <operator>.
|
||||
message Toleration {
|
||||
// Required. Key is the taint key that the toleration applies to.
|
||||
optional string key = 1;
|
||||
|
||||
// operator represents a key's relationship to the value.
|
||||
// Valid operators are Exists and Equal. Defaults to Equal.
|
||||
// Exists is equivalent to wildcard for value, so that a pod can
|
||||
// tolerate all taints of a particular category.
|
||||
optional string operator = 2;
|
||||
|
||||
// Value is the taint value the toleration matches to.
|
||||
// If the operator is Exists, the value should be empty, otherwise just a regular string.
|
||||
optional string value = 3;
|
||||
|
||||
// Effect indicates the taint effect to match. Empty means match all taint effects.
|
||||
// When specified, allowed values are NoSchedule and PreferNoSchedule.
|
||||
optional string effect = 4;
|
||||
}
|
||||
|
||||
// Volume represents a named volume in a pod that may be accessed by any container in the pod.
|
||||
message Volume {
|
||||
// Volume's name.
|
||||
|
|
|
@ -23801,6 +23801,592 @@ func (x *PreferredSchedulingTerm) codecDecodeSelfFromArray(l int, d *codec1978.D
|
|||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
}
|
||||
|
||||
func (x *Taint) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperEncoder(e)
|
||||
_, _, _ = h, z, r
|
||||
if x == nil {
|
||||
r.EncodeNil()
|
||||
} else {
|
||||
yym1 := z.EncBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.EncExt(x) {
|
||||
} else {
|
||||
yysep2 := !z.EncBinary()
|
||||
yy2arr2 := z.EncBasicHandle().StructToArray
|
||||
var yyq2 [3]bool
|
||||
_, _, _ = yysep2, yyq2, yy2arr2
|
||||
const yyr2 bool = false
|
||||
yyq2[1] = x.Value != ""
|
||||
var yynn2 int
|
||||
if yyr2 || yy2arr2 {
|
||||
r.EncodeArrayStart(3)
|
||||
} else {
|
||||
yynn2 = 2
|
||||
for _, b := range yyq2 {
|
||||
if b {
|
||||
yynn2++
|
||||
}
|
||||
}
|
||||
r.EncodeMapStart(yynn2)
|
||||
yynn2 = 0
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
yym4 := z.EncBinary()
|
||||
_ = yym4
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Key))
|
||||
}
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("key"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
yym5 := z.EncBinary()
|
||||
_ = yym5
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Key))
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if yyq2[1] {
|
||||
yym7 := z.EncBinary()
|
||||
_ = yym7
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Value))
|
||||
}
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[1] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("value"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
yym8 := z.EncBinary()
|
||||
_ = yym8
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Value))
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
x.Effect.CodecEncodeSelf(e)
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("effect"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
x.Effect.CodecEncodeSelf(e)
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Taint) CodecDecodeSelf(d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
yym1 := z.DecBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.DecExt(x) {
|
||||
} else {
|
||||
yyct2 := r.ContainerType()
|
||||
if yyct2 == codecSelferValueTypeMap1234 {
|
||||
yyl2 := r.ReadMapStart()
|
||||
if yyl2 == 0 {
|
||||
z.DecSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
} else {
|
||||
x.codecDecodeSelfFromMap(yyl2, d)
|
||||
}
|
||||
} else if yyct2 == codecSelferValueTypeArray1234 {
|
||||
yyl2 := r.ReadArrayStart()
|
||||
if yyl2 == 0 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
} else {
|
||||
x.codecDecodeSelfFromArray(yyl2, d)
|
||||
}
|
||||
} else {
|
||||
panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Taint) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yys3Slc = z.DecScratchBuffer() // default slice to decode into
|
||||
_ = yys3Slc
|
||||
var yyhl3 bool = l >= 0
|
||||
for yyj3 := 0; ; yyj3++ {
|
||||
if yyhl3 {
|
||||
if yyj3 >= l {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if r.CheckBreak() {
|
||||
break
|
||||
}
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerMapKey1234)
|
||||
yys3Slc = r.DecodeBytes(yys3Slc, true, true)
|
||||
yys3 := string(yys3Slc)
|
||||
z.DecSendContainerState(codecSelfer_containerMapValue1234)
|
||||
switch yys3 {
|
||||
case "key":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Key = ""
|
||||
} else {
|
||||
x.Key = string(r.DecodeString())
|
||||
}
|
||||
case "value":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Value = ""
|
||||
} else {
|
||||
x.Value = string(r.DecodeString())
|
||||
}
|
||||
case "effect":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Effect = ""
|
||||
} else {
|
||||
x.Effect = TaintEffect(r.DecodeString())
|
||||
}
|
||||
default:
|
||||
z.DecStructFieldNotFound(-1, yys3)
|
||||
} // end switch yys3
|
||||
} // end for yyj3
|
||||
z.DecSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
}
|
||||
|
||||
func (x *Taint) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yyj7 int
|
||||
var yyb7 bool
|
||||
var yyhl7 bool = l >= 0
|
||||
yyj7++
|
||||
if yyhl7 {
|
||||
yyb7 = yyj7 > l
|
||||
} else {
|
||||
yyb7 = r.CheckBreak()
|
||||
}
|
||||
if yyb7 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Key = ""
|
||||
} else {
|
||||
x.Key = string(r.DecodeString())
|
||||
}
|
||||
yyj7++
|
||||
if yyhl7 {
|
||||
yyb7 = yyj7 > l
|
||||
} else {
|
||||
yyb7 = r.CheckBreak()
|
||||
}
|
||||
if yyb7 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Value = ""
|
||||
} else {
|
||||
x.Value = string(r.DecodeString())
|
||||
}
|
||||
yyj7++
|
||||
if yyhl7 {
|
||||
yyb7 = yyj7 > l
|
||||
} else {
|
||||
yyb7 = r.CheckBreak()
|
||||
}
|
||||
if yyb7 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Effect = ""
|
||||
} else {
|
||||
x.Effect = TaintEffect(r.DecodeString())
|
||||
}
|
||||
for {
|
||||
yyj7++
|
||||
if yyhl7 {
|
||||
yyb7 = yyj7 > l
|
||||
} else {
|
||||
yyb7 = r.CheckBreak()
|
||||
}
|
||||
if yyb7 {
|
||||
break
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
z.DecStructFieldNotFound(yyj7-1, "")
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
}
|
||||
|
||||
func (x TaintEffect) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperEncoder(e)
|
||||
_, _, _ = h, z, r
|
||||
yym1 := z.EncBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.EncExt(x) {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x))
|
||||
}
|
||||
}
|
||||
|
||||
func (x *TaintEffect) CodecDecodeSelf(d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
yym1 := z.DecBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.DecExt(x) {
|
||||
} else {
|
||||
*((*string)(x)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Toleration) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperEncoder(e)
|
||||
_, _, _ = h, z, r
|
||||
if x == nil {
|
||||
r.EncodeNil()
|
||||
} else {
|
||||
yym1 := z.EncBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.EncExt(x) {
|
||||
} else {
|
||||
yysep2 := !z.EncBinary()
|
||||
yy2arr2 := z.EncBasicHandle().StructToArray
|
||||
var yyq2 [4]bool
|
||||
_, _, _ = yysep2, yyq2, yy2arr2
|
||||
const yyr2 bool = false
|
||||
yyq2[0] = x.Key != ""
|
||||
yyq2[1] = x.Operator != ""
|
||||
yyq2[2] = x.Value != ""
|
||||
yyq2[3] = x.Effect != ""
|
||||
var yynn2 int
|
||||
if yyr2 || yy2arr2 {
|
||||
r.EncodeArrayStart(4)
|
||||
} else {
|
||||
yynn2 = 0
|
||||
for _, b := range yyq2 {
|
||||
if b {
|
||||
yynn2++
|
||||
}
|
||||
}
|
||||
r.EncodeMapStart(yynn2)
|
||||
yynn2 = 0
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if yyq2[0] {
|
||||
yym4 := z.EncBinary()
|
||||
_ = yym4
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Key))
|
||||
}
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[0] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("key"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
yym5 := z.EncBinary()
|
||||
_ = yym5
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Key))
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if yyq2[1] {
|
||||
x.Operator.CodecEncodeSelf(e)
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[1] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("operator"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
x.Operator.CodecEncodeSelf(e)
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if yyq2[2] {
|
||||
yym10 := z.EncBinary()
|
||||
_ = yym10
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Value))
|
||||
}
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[2] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("value"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
yym11 := z.EncBinary()
|
||||
_ = yym11
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Value))
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if yyq2[3] {
|
||||
x.Effect.CodecEncodeSelf(e)
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[3] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("effect"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
x.Effect.CodecEncodeSelf(e)
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Toleration) CodecDecodeSelf(d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
yym1 := z.DecBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.DecExt(x) {
|
||||
} else {
|
||||
yyct2 := r.ContainerType()
|
||||
if yyct2 == codecSelferValueTypeMap1234 {
|
||||
yyl2 := r.ReadMapStart()
|
||||
if yyl2 == 0 {
|
||||
z.DecSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
} else {
|
||||
x.codecDecodeSelfFromMap(yyl2, d)
|
||||
}
|
||||
} else if yyct2 == codecSelferValueTypeArray1234 {
|
||||
yyl2 := r.ReadArrayStart()
|
||||
if yyl2 == 0 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
} else {
|
||||
x.codecDecodeSelfFromArray(yyl2, d)
|
||||
}
|
||||
} else {
|
||||
panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Toleration) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yys3Slc = z.DecScratchBuffer() // default slice to decode into
|
||||
_ = yys3Slc
|
||||
var yyhl3 bool = l >= 0
|
||||
for yyj3 := 0; ; yyj3++ {
|
||||
if yyhl3 {
|
||||
if yyj3 >= l {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if r.CheckBreak() {
|
||||
break
|
||||
}
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerMapKey1234)
|
||||
yys3Slc = r.DecodeBytes(yys3Slc, true, true)
|
||||
yys3 := string(yys3Slc)
|
||||
z.DecSendContainerState(codecSelfer_containerMapValue1234)
|
||||
switch yys3 {
|
||||
case "key":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Key = ""
|
||||
} else {
|
||||
x.Key = string(r.DecodeString())
|
||||
}
|
||||
case "operator":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Operator = ""
|
||||
} else {
|
||||
x.Operator = TolerationOperator(r.DecodeString())
|
||||
}
|
||||
case "value":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Value = ""
|
||||
} else {
|
||||
x.Value = string(r.DecodeString())
|
||||
}
|
||||
case "effect":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Effect = ""
|
||||
} else {
|
||||
x.Effect = TaintEffect(r.DecodeString())
|
||||
}
|
||||
default:
|
||||
z.DecStructFieldNotFound(-1, yys3)
|
||||
} // end switch yys3
|
||||
} // end for yyj3
|
||||
z.DecSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
}
|
||||
|
||||
func (x *Toleration) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yyj8 int
|
||||
var yyb8 bool
|
||||
var yyhl8 bool = l >= 0
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb8 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Key = ""
|
||||
} else {
|
||||
x.Key = string(r.DecodeString())
|
||||
}
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb8 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Operator = ""
|
||||
} else {
|
||||
x.Operator = TolerationOperator(r.DecodeString())
|
||||
}
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb8 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Value = ""
|
||||
} else {
|
||||
x.Value = string(r.DecodeString())
|
||||
}
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb8 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Effect = ""
|
||||
} else {
|
||||
x.Effect = TaintEffect(r.DecodeString())
|
||||
}
|
||||
for {
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb8 {
|
||||
break
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
z.DecStructFieldNotFound(yyj8-1, "")
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
}
|
||||
|
||||
func (x TolerationOperator) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperEncoder(e)
|
||||
_, _, _ = h, z, r
|
||||
yym1 := z.EncBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.EncExt(x) {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x))
|
||||
}
|
||||
}
|
||||
|
||||
func (x *TolerationOperator) CodecDecodeSelf(d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
yym1 := z.DecBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.DecExt(x) {
|
||||
} else {
|
||||
*((*string)(x)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
|
||||
func (x *PodSpec) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperEncoder(e)
|
||||
|
|
|
@ -1541,6 +1541,73 @@ type PreferredSchedulingTerm struct {
|
|||
Preference NodeSelectorTerm `json:"preference" protobuf:"bytes,2,opt,name=preference"`
|
||||
}
|
||||
|
||||
// The node this Taint is attached to has the effect "effect" on
|
||||
// any pod that that does not tolerate the Taint.
|
||||
type Taint struct {
|
||||
// Required. The taint key to be applied to a node.
|
||||
Key string `json:"key" patchStrategy:"merge" patchMergeKey:"key" protobuf:"bytes,1,opt,name=key"`
|
||||
// Required. The taint value corresponding to the taint key.
|
||||
Value string `json:"value,omitempty" protobuf:"bytes,2,opt,name=value"`
|
||||
// Required. The effect of the taint on pods
|
||||
// that do not tolerate the taint.
|
||||
// Valid effects are NoSchedule and PreferNoSchedule.
|
||||
Effect TaintEffect `json:"effect" protobuf:"bytes,3,opt,name=effect,casttype=TaintEffect"`
|
||||
}
|
||||
|
||||
type TaintEffect string
|
||||
|
||||
const (
|
||||
// Do not allow new pods to schedule onto the node unless they tolerate the taint,
|
||||
// but allow all pods submitted to Kubelet without going through the scheduler
|
||||
// to start, and allow all already-running pods to continue running.
|
||||
// Enforced by the scheduler.
|
||||
TaintEffectNoSchedule TaintEffect = "NoSchedule"
|
||||
// Like TaintEffectNoSchedule, but the scheduler tries not to schedule
|
||||
// new pods onto the node, rather than prohibiting new pods from scheduling
|
||||
// onto the node entirely. Enforced by the scheduler.
|
||||
TaintEffectPreferNoSchedule TaintEffect = "PreferNoSchedule"
|
||||
// NOT YET IMPLEMENTED. TODO: Uncomment field once it is implemented.
|
||||
// Do not allow new pods to schedule onto the node unless they tolerate the taint,
|
||||
// do not allow pods to start on Kubelet unless they tolerate the taint,
|
||||
// but allow all already-running pods to continue running.
|
||||
// Enforced by the scheduler and Kubelet.
|
||||
// TaintEffectNoScheduleNoAdmit TaintEffect = "NoScheduleNoAdmit"
|
||||
// NOT YET IMPLEMENTED. TODO: Uncomment field once it is implemented.
|
||||
// Do not allow new pods to schedule onto the node unless they tolerate the taint,
|
||||
// do not allow pods to start on Kubelet unless they tolerate the taint,
|
||||
// and evict any already-running pods that do not tolerate the taint.
|
||||
// Enforced by the scheduler and Kubelet.
|
||||
// TaintEffectNoScheduleNoAdmitNoExecute = "NoScheduleNoAdmitNoExecute"
|
||||
)
|
||||
|
||||
// The pod this Toleration is attached to tolerates any taint that matches
|
||||
// the triple <key,value,effect> using the matching operator <operator>.
|
||||
type Toleration struct {
|
||||
// Required. Key is the taint key that the toleration applies to.
|
||||
Key string `json:"key,omitempty" patchStrategy:"merge" patchMergeKey:"key" protobuf:"bytes,1,opt,name=key"`
|
||||
// operator represents a key's relationship to the value.
|
||||
// Valid operators are Exists and Equal. Defaults to Equal.
|
||||
// Exists is equivalent to wildcard for value, so that a pod can
|
||||
// tolerate all taints of a particular category.
|
||||
Operator TolerationOperator `json:"operator,omitempty" protobuf:"bytes,2,opt,name=operator,casttype=TolerationOperator"`
|
||||
// Value is the taint value the toleration matches to.
|
||||
// If the operator is Exists, the value should be empty, otherwise just a regular string.
|
||||
Value string `json:"value,omitempty" protobuf:"bytes,3,opt,name=value"`
|
||||
// Effect indicates the taint effect to match. Empty means match all taint effects.
|
||||
// When specified, allowed values are NoSchedule and PreferNoSchedule.
|
||||
Effect TaintEffect `json:"effect,omitempty" protobuf:"bytes,4,opt,name=effect,casttype=TaintEffect"`
|
||||
// TODO: For forgiveness (#1574), we'd eventually add at least a grace period
|
||||
// here, and possibly an occurrence threshold and period.
|
||||
}
|
||||
|
||||
// A toleration operator is the set of operators that can be used in a toleration.
|
||||
type TolerationOperator string
|
||||
|
||||
const (
|
||||
TolerationOpExists TolerationOperator = "Exists"
|
||||
TolerationOpEqual TolerationOperator = "Equal"
|
||||
)
|
||||
|
||||
const (
|
||||
// This annotation key will be used to contain an array of v1 JSON encoded Containers
|
||||
// for init containers. The annotation will be placed into the internal type and cleared.
|
||||
|
|
|
@ -1616,6 +1616,29 @@ func (TCPSocketAction) SwaggerDoc() map[string]string {
|
|||
return map_TCPSocketAction
|
||||
}
|
||||
|
||||
var map_Taint = map[string]string{
|
||||
"": "The node this Taint is attached to has the effect \"effect\" on any pod that that does not tolerate the Taint.",
|
||||
"key": "Required. The taint key to be applied to a node.",
|
||||
"value": "Required. The taint value corresponding to the taint key.",
|
||||
"effect": "Required. The effect of the taint on pods that do not tolerate the taint. Valid effects are NoSchedule and PreferNoSchedule.",
|
||||
}
|
||||
|
||||
func (Taint) SwaggerDoc() map[string]string {
|
||||
return map_Taint
|
||||
}
|
||||
|
||||
var map_Toleration = map[string]string{
|
||||
"": "The pod this Toleration is attached to tolerates any taint that matches the triple <key,value,effect> using the matching operator <operator>.",
|
||||
"key": "Required. Key is the taint key that the toleration applies to.",
|
||||
"operator": "operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.",
|
||||
"value": "Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.",
|
||||
"effect": "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule and PreferNoSchedule.",
|
||||
}
|
||||
|
||||
func (Toleration) SwaggerDoc() map[string]string {
|
||||
return map_Toleration
|
||||
}
|
||||
|
||||
var map_Volume = map[string]string{
|
||||
"": "Volume represents a named volume in a pod that may be accessed by any container in the pod.",
|
||||
"name": "Volume's name. Must be a DNS_LABEL and unique within the pod. More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names",
|
||||
|
|
|
@ -109,6 +109,10 @@ func ValidatePodSpecificAnnotations(annotations map[string]string, fldPath *fiel
|
|||
allErrs = append(allErrs, ValidateAffinityInPodAnnotations(annotations, fldPath)...)
|
||||
}
|
||||
|
||||
if annotations[api.TolerationsAnnotationKey] != "" {
|
||||
allErrs = append(allErrs, ValidateTolerationsInPodAnnotations(annotations, fldPath)...)
|
||||
}
|
||||
|
||||
if hostname, exists := annotations[utilpod.PodHostnameAnnotation]; exists && !validation.IsDNS1123Label(hostname) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, utilpod.PodHostnameAnnotation, DNS1123LabelErrorMsg))
|
||||
}
|
||||
|
@ -1462,6 +1466,60 @@ func validateImagePullSecrets(imagePullSecrets []api.LocalObjectReference, fldPa
|
|||
return allErrors
|
||||
}
|
||||
|
||||
func validateTaintEffect(effect *api.TaintEffect, allowEmpty bool, fldPath *field.Path) field.ErrorList {
|
||||
if !allowEmpty && len(*effect) == 0 {
|
||||
return field.ErrorList{field.Required(fldPath, "")}
|
||||
}
|
||||
|
||||
allErrors := field.ErrorList{}
|
||||
switch *effect {
|
||||
// TODO: Replace next line with subsequent commented-out line when implement TaintEffectNoScheduleNoAdmit, TaintEffectNoScheduleNoAdmitNoExecute.
|
||||
case api.TaintEffectNoSchedule, api.TaintEffectPreferNoSchedule:
|
||||
// case api.TaintEffectNoSchedule, api.TaintEffectPreferNoSchedule, api.TaintEffectNoScheduleNoAdmit, api.TaintEffectNoScheduleNoAdmitNoExecute:
|
||||
default:
|
||||
validValues := []string{
|
||||
string(api.TaintEffectNoSchedule),
|
||||
string(api.TaintEffectPreferNoSchedule),
|
||||
// TODO: Uncomment this block when implement TaintEffectNoScheduleNoAdmit, TaintEffectNoScheduleNoAdmitNoExecute.
|
||||
// string(api.TaintEffectNoScheduleNoAdmit),
|
||||
// string(api.TaintEffectNoScheduleNoAdmitNoExecute),
|
||||
}
|
||||
allErrors = append(allErrors, field.NotSupported(fldPath, effect, validValues))
|
||||
}
|
||||
return allErrors
|
||||
}
|
||||
|
||||
// validateTolerations tests if given tolerations have valid data.
|
||||
func validateTolerations(tolerations []api.Toleration, fldPath *field.Path) field.ErrorList {
|
||||
allErrors := field.ErrorList{}
|
||||
for i, toleration := range tolerations {
|
||||
idxPath := fldPath.Index(i)
|
||||
// validate the toleration key
|
||||
allErrors = append(allErrors, unversionedvalidation.ValidateLabelName(toleration.Key, idxPath.Child("key"))...)
|
||||
|
||||
// validate toleration operator and value
|
||||
switch toleration.Operator {
|
||||
case api.TolerationOpEqual, "":
|
||||
if errs := validation.IsValidLabelValue(toleration.Value); len(errs) != 0 {
|
||||
allErrors = append(allErrors, field.Invalid(idxPath.Child("operator"), toleration.Value, strings.Join(errs, ";")))
|
||||
}
|
||||
case api.TolerationOpExists:
|
||||
if len(toleration.Value) > 0 {
|
||||
allErrors = append(allErrors, field.Invalid(idxPath.Child("operator"), toleration, "value must be empty when `operator` is 'Exists'"))
|
||||
}
|
||||
default:
|
||||
validValues := []string{string(api.TolerationOpEqual), string(api.TolerationOpExists)}
|
||||
allErrors = append(allErrors, field.NotSupported(idxPath.Child("operator"), toleration.Operator, validValues))
|
||||
}
|
||||
|
||||
// validate toleration effect
|
||||
if len(toleration.Effect) > 0 {
|
||||
allErrors = append(allErrors, validateTaintEffect(&toleration.Effect, true, idxPath.Child("effect"))...)
|
||||
}
|
||||
}
|
||||
return allErrors
|
||||
}
|
||||
|
||||
// ValidatePod tests if required fields in the pod are set.
|
||||
func ValidatePod(pod *api.Pod) field.ErrorList {
|
||||
fldPath := field.NewPath("metadata")
|
||||
|
@ -1701,6 +1759,22 @@ func ValidateAffinityInPodAnnotations(annotations map[string]string, fldPath *fi
|
|||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateTolerationsInPodAnnotations tests that the serialized tolerations in Pod.Annotations has valid data
|
||||
func ValidateTolerationsInPodAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
tolerations, err := api.GetTolerationsFromPodAnnotations(annotations)
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, api.TolerationsAnnotationKey, err.Error()))
|
||||
return allErrs
|
||||
}
|
||||
if len(tolerations) > 0 {
|
||||
allErrs = append(allErrs, validateTolerations(tolerations, fldPath.Child(api.TolerationsAnnotationKey))...)
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePodSecurityContext test that the specified PodSecurityContext has valid data.
|
||||
func ValidatePodSecurityContext(securityContext *api.PodSecurityContext, spec *api.PodSpec, specPath, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
@ -2113,9 +2187,51 @@ func ValidateReadOnlyPersistentDisks(volumes []api.Volume, fldPath *field.Path)
|
|||
return allErrs
|
||||
}
|
||||
|
||||
// validateTaints tests if given taints have valid data.
|
||||
func validateTaints(taints []api.Taint, fldPath *field.Path) field.ErrorList {
|
||||
allErrors := field.ErrorList{}
|
||||
for i, currTaint := range taints {
|
||||
idxPath := fldPath.Index(i)
|
||||
// validate the taint key
|
||||
allErrors = append(allErrors, unversionedvalidation.ValidateLabelName(currTaint.Key, idxPath.Child("key"))...)
|
||||
// validate the taint value
|
||||
if errs := validation.IsValidLabelValue(currTaint.Value); len(errs) != 0 {
|
||||
allErrors = append(allErrors, field.Invalid(idxPath.Child("value"), currTaint.Value, strings.Join(errs, ";")))
|
||||
}
|
||||
// validate the taint effect
|
||||
allErrors = append(allErrors, validateTaintEffect(&currTaint.Effect, false, idxPath.Child("effect"))...)
|
||||
}
|
||||
return allErrors
|
||||
}
|
||||
|
||||
// ValidateTaintsInNodeAnnotations tests that the serialized taints in Node.Annotations has valid data
|
||||
func ValidateTaintsInNodeAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
taints, err := api.GetTaintsFromNodeAnnotations(annotations)
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, api.TaintsAnnotationKey, err.Error()))
|
||||
return allErrs
|
||||
}
|
||||
if len(taints) > 0 {
|
||||
allErrs = append(allErrs, validateTaints(taints, fldPath.Child(api.TaintsAnnotationKey))...)
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateNodeSpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
|
||||
if annotations[api.TaintsAnnotationKey] != "" {
|
||||
return ValidateTaintsInNodeAnnotations(annotations, fldPath)
|
||||
}
|
||||
return field.ErrorList{}
|
||||
}
|
||||
|
||||
// ValidateNode tests if required fields in the node are set.
|
||||
func ValidateNode(node *api.Node) field.ErrorList {
|
||||
allErrs := ValidateObjectMeta(&node.ObjectMeta, false, ValidateNodeName, field.NewPath("metadata"))
|
||||
fldPath := field.NewPath("metadata")
|
||||
allErrs := ValidateObjectMeta(&node.ObjectMeta, false, ValidateNodeName, fldPath)
|
||||
allErrs = append(allErrs, ValidateNodeSpecificAnnotations(node.ObjectMeta.Annotations, fldPath.Child("annotations"))...)
|
||||
|
||||
// Only validate spec. All status fields are optional and can be updated later.
|
||||
|
||||
|
@ -2130,7 +2246,9 @@ func ValidateNode(node *api.Node) field.ErrorList {
|
|||
|
||||
// ValidateNodeUpdate tests to make sure a node update can be applied. Modifies oldNode.
|
||||
func ValidateNodeUpdate(node, oldNode *api.Node) field.ErrorList {
|
||||
allErrs := ValidateObjectMetaUpdate(&node.ObjectMeta, &oldNode.ObjectMeta, field.NewPath("metadata"))
|
||||
fldPath := field.NewPath("metadata")
|
||||
allErrs := ValidateObjectMetaUpdate(&node.ObjectMeta, &oldNode.ObjectMeta, fldPath)
|
||||
allErrs = append(allErrs, ValidateNodeSpecificAnnotations(node.ObjectMeta.Annotations, fldPath.Child("annotations"))...)
|
||||
|
||||
// TODO: Enable the code once we have better api object.status update model. Currently,
|
||||
// anyone can update node status.
|
||||
|
|
|
@ -1954,60 +1954,6 @@ func TestValidatePod(t *testing.T) {
|
|||
NodeName: "foobar",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, pod := range successCases {
|
||||
if errs := ValidatePod(&pod); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
errorCases := map[string]api.Pod{
|
||||
"bad name": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: "ns"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
},
|
||||
"bad namespace": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: ""},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
},
|
||||
"bad spec": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "ns"},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{}},
|
||||
},
|
||||
},
|
||||
"bad label": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc",
|
||||
Namespace: "ns",
|
||||
Labels: map[string]string{
|
||||
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
for k, v := range errorCases {
|
||||
if errs := ValidatePod(&v); len(errs) == 0 {
|
||||
t.Errorf("expected failure for %q", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAffinity(t *testing.T) {
|
||||
successCases := []api.Pod{
|
||||
{ // Serialized affinity requirements in annotations.
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "123",
|
||||
|
@ -2160,6 +2106,83 @@ func TestValidateAffinity(t *testing.T) {
|
|||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{ // populate tolerations equal operator in annotations.
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
Annotations: map[string]string{
|
||||
api.TolerationsAnnotationKey: `
|
||||
[{
|
||||
"key": "foo",
|
||||
"operator": "Equal",
|
||||
"value": "bar",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{ // populate tolerations exists operator in annotations.
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
Annotations: map[string]string{
|
||||
api.TolerationsAnnotationKey: `
|
||||
[{
|
||||
"key": "foo",
|
||||
"operator": "Exists",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{ // empty operator is ok for toleration
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
Annotations: map[string]string{
|
||||
api.TolerationsAnnotationKey: `
|
||||
[{
|
||||
"key": "foo",
|
||||
"value": "bar",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{ // empty efffect is ok for toleration
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
Annotations: map[string]string{
|
||||
api.TolerationsAnnotationKey: `
|
||||
[{
|
||||
"key": "foo",
|
||||
"operator": "Equal",
|
||||
"value": "bar"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, pod := range successCases {
|
||||
if errs := ValidatePod(&pod); len(errs) != 0 {
|
||||
|
@ -2168,6 +2191,42 @@ func TestValidateAffinity(t *testing.T) {
|
|||
}
|
||||
|
||||
errorCases := map[string]api.Pod{
|
||||
"bad name": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: "ns"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
},
|
||||
"bad namespace": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: ""},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
},
|
||||
"bad spec": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "ns"},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{}},
|
||||
},
|
||||
},
|
||||
"bad label": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc",
|
||||
Namespace: "ns",
|
||||
Labels: map[string]string{
|
||||
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
},
|
||||
"invalid json of node affinity in pod annotations": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "123",
|
||||
|
@ -2476,6 +2535,66 @@ func TestValidateAffinity(t *testing.T) {
|
|||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
"invalid toleration key": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
Annotations: map[string]string{
|
||||
api.TolerationsAnnotationKey: `
|
||||
[{
|
||||
"key": "nospecialchars^=@",
|
||||
"operator": "Equal",
|
||||
"value": "bar",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
"invalid toleration operator": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
Annotations: map[string]string{
|
||||
api.TolerationsAnnotationKey: `
|
||||
[{
|
||||
"key": "foo",
|
||||
"operator": "In",
|
||||
"value": "bar",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
"value must be empty when `operator` is 'Exists'": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
Annotations: map[string]string{
|
||||
api.TolerationsAnnotationKey: `
|
||||
[{
|
||||
"key": "foo",
|
||||
"operator": "Exists",
|
||||
"value": "bar",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
}
|
||||
for k, v := range errorCases {
|
||||
if errs := ValidatePod(&v); len(errs) == 0 {
|
||||
|
@ -3885,6 +4004,32 @@ func TestValidateNode(t *testing.T) {
|
|||
ExternalID: "external",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "dedicated-node1",
|
||||
// Add a valid taint to a node
|
||||
Annotations: map[string]string{
|
||||
api.TaintsAnnotationKey: `
|
||||
[{
|
||||
"key": "GPU",
|
||||
"value": "true",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Status: api.NodeStatus{
|
||||
Addresses: []api.NodeAddress{
|
||||
{Type: api.NodeLegacyHostIP, Address: "something"},
|
||||
},
|
||||
Capacity: api.ResourceList{
|
||||
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
|
||||
api.ResourceName(api.ResourceMemory): resource.MustParse("0"),
|
||||
},
|
||||
},
|
||||
Spec: api.NodeSpec{
|
||||
ExternalID: "external",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, successCase := range successCases {
|
||||
if errs := ValidateNode(&successCase); len(errs) != 0 {
|
||||
|
@ -3936,6 +4081,119 @@ func TestValidateNode(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
"missing-taint-key": {
|
||||
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "dedicated-node1",
|
||||
// Add a taint with an empty key to a node
|
||||
Annotations: map[string]string{
|
||||
api.TaintsAnnotationKey: `
|
||||
[{
|
||||
"key": "",
|
||||
"value": "special-user-1",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Spec: api.NodeSpec{
|
||||
ExternalID: "external",
|
||||
},
|
||||
},
|
||||
"bad-taint-key": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "dedicated-node1",
|
||||
// Add a taint with an empty key to a node
|
||||
Annotations: map[string]string{
|
||||
api.TaintsAnnotationKey: `
|
||||
[{
|
||||
"key": "NoUppercaseOrSpecialCharsLike=Equals",
|
||||
"value": "special-user-1",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Spec: api.NodeSpec{
|
||||
ExternalID: "external",
|
||||
},
|
||||
},
|
||||
"bad-taint-value": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "dedicated-node2",
|
||||
Annotations: map[string]string{
|
||||
api.TaintsAnnotationKey: `
|
||||
[{
|
||||
"key": "dedicated",
|
||||
"value": "some\\bad\\value",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Status: api.NodeStatus{
|
||||
Addresses: []api.NodeAddress{
|
||||
{Type: api.NodeLegacyHostIP, Address: "something"},
|
||||
},
|
||||
Capacity: api.ResourceList{
|
||||
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
|
||||
api.ResourceName(api.ResourceMemory): resource.MustParse("0"),
|
||||
},
|
||||
},
|
||||
// Add a taint with an empty value to a node
|
||||
Spec: api.NodeSpec{
|
||||
ExternalID: "external",
|
||||
},
|
||||
},
|
||||
"missing-taint-effect": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "dedicated-node3",
|
||||
// Add a taint with an empty effect to a node
|
||||
Annotations: map[string]string{
|
||||
api.TaintsAnnotationKey: `
|
||||
[{
|
||||
"key": "dedicated",
|
||||
"value": "special-user-3",
|
||||
"effect": ""
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Status: api.NodeStatus{
|
||||
Addresses: []api.NodeAddress{
|
||||
{Type: api.NodeLegacyHostIP, Address: "something"},
|
||||
},
|
||||
Capacity: api.ResourceList{
|
||||
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
|
||||
api.ResourceName(api.ResourceMemory): resource.MustParse("0"),
|
||||
},
|
||||
},
|
||||
Spec: api.NodeSpec{
|
||||
ExternalID: "external",
|
||||
},
|
||||
},
|
||||
"invalide-taint-effect": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "dedicated-node3",
|
||||
// Add a taint with an empty effect to a node
|
||||
Annotations: map[string]string{
|
||||
api.TaintsAnnotationKey: `
|
||||
[{
|
||||
"key": "dedicated",
|
||||
"value": "special-user-3",
|
||||
"effect": "NoExecute"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Status: api.NodeStatus{
|
||||
Addresses: []api.NodeAddress{
|
||||
{Type: api.NodeLegacyHostIP, Address: "something"},
|
||||
},
|
||||
Capacity: api.ResourceList{
|
||||
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
|
||||
api.ResourceName(api.ResourceMemory): resource.MustParse("0"),
|
||||
},
|
||||
},
|
||||
Spec: api.NodeSpec{
|
||||
ExternalID: "external",
|
||||
},
|
||||
},
|
||||
}
|
||||
for k, v := range errorCases {
|
||||
errs := ValidateNode(&v)
|
||||
|
@ -3945,14 +4203,19 @@ func TestValidateNode(t *testing.T) {
|
|||
for i := range errs {
|
||||
field := errs[i].Field
|
||||
expectedFields := map[string]bool{
|
||||
"metadata.name": true,
|
||||
"metadata.labels": true,
|
||||
"metadata.annotations": true,
|
||||
"metadata.namespace": true,
|
||||
"spec.externalID": true,
|
||||
"metadata.name": true,
|
||||
"metadata.labels": true,
|
||||
"metadata.annotations": true,
|
||||
"metadata.namespace": true,
|
||||
"spec.externalID": true,
|
||||
"metadata.annotations.scheduler.alpha.kubernetes.io/taints[0].key": true,
|
||||
"metadata.annotations.scheduler.alpha.kubernetes.io/taints[0].value": true,
|
||||
"metadata.annotations.scheduler.alpha.kubernetes.io/taints[0].effect": true,
|
||||
}
|
||||
if expectedFields[field] == false {
|
||||
t.Errorf("%s: missing prefix for: %v", k, errs[i])
|
||||
if val, ok := expectedFields[field]; ok {
|
||||
if !val {
|
||||
t.Errorf("%s: missing prefix for: %v", k, errs[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -227,6 +227,7 @@ Find more information at https://github.com/kubernetes/kubernetes.`,
|
|||
|
||||
cmds.AddCommand(NewCmdLabel(f, out))
|
||||
cmds.AddCommand(NewCmdAnnotate(f, out))
|
||||
cmds.AddCommand(NewCmdTaint(f, out))
|
||||
|
||||
cmds.AddCommand(cmdconfig.NewCmdConfig(clientcmd.NewDefaultPathOptions(), out))
|
||||
cmds.AddCommand(NewCmdClusterInfo(f, out))
|
||||
|
|
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"encoding/json"
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/util/strategicpatch"
|
||||
"k8s.io/kubernetes/pkg/util/validation"
|
||||
)
|
||||
|
||||
// TaintOptions have the data required to perform the taint operation
|
||||
type TaintOptions struct {
|
||||
resources []string
|
||||
taintsToAdd []api.Taint
|
||||
removeTaintKeys []string
|
||||
builder *resource.Builder
|
||||
selector string
|
||||
overwrite bool
|
||||
all bool
|
||||
f *cmdutil.Factory
|
||||
out io.Writer
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
const (
|
||||
taint_long = `Update the taints on one or more nodes.
|
||||
|
||||
A taint consists of a key, value, and effect. As an argument here, it is expressed as key=value:effect.
|
||||
The key must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to %[1]d characters.
|
||||
The value must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to %[1]d characters.
|
||||
The effect must be NoSchedule or PreferNoSchedule.
|
||||
Currently taint can only apply to node.`
|
||||
taint_example = `# Update node 'foo' with a taint with key 'dedicated' and value 'special-user' and effect 'NoSchedule'.
|
||||
# If a taint with that key already exists, its value and effect are replaced as specified.
|
||||
kubectl taint nodes foo dedicated=special-user:NoSchedule
|
||||
# Remove from node 'foo' the taint with key 'dedicated' if one exists.
|
||||
kubectl taint nodes foo dedicated-`
|
||||
)
|
||||
|
||||
func NewCmdTaint(f *cmdutil.Factory, out io.Writer) *cobra.Command {
|
||||
options := &TaintOptions{}
|
||||
|
||||
// retrieve a list of handled resources from printer as valid args
|
||||
validArgs := []string{}
|
||||
p, err := f.Printer(nil, false, false, false, false, false, false, []string{})
|
||||
cmdutil.CheckErr(err)
|
||||
if p != nil {
|
||||
validArgs = p.HandledResources()
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "taint NODE NAME KEY_1=VAL_1:TAINT_EFFECT_1 ... KEY_N=VAL_N:TAINT_EFFECT_N",
|
||||
Short: "Update the taints on one or more nodes",
|
||||
Long: fmt.Sprintf(taint_long, validation.DNS1123SubdomainMaxLength, validation.LabelValueMaxLength),
|
||||
Example: taint_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := options.Complete(f, out, cmd, args); err != nil {
|
||||
cmdutil.CheckErr(err)
|
||||
}
|
||||
if err := options.Validate(args); err != nil {
|
||||
cmdutil.CheckErr(cmdutil.UsageError(cmd, err.Error()))
|
||||
}
|
||||
if err := options.RunTaint(); err != nil {
|
||||
cmdutil.CheckErr(err)
|
||||
}
|
||||
},
|
||||
ValidArgs: validArgs,
|
||||
}
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddInclude3rdPartyFlags(cmd)
|
||||
cmd.Flags().StringVarP(&options.selector, "selector", "l", "", "Selector (label query) to filter on")
|
||||
cmd.Flags().BoolVar(&options.overwrite, "overwrite", false, "If true, allow taints to be overwritten, otherwise reject taint updates that overwrite existing taints.")
|
||||
cmd.Flags().BoolVar(&options.all, "all", false, "select all nodes in the cluster")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func deleteTaintByKey(taints []api.Taint, key string) ([]api.Taint, error) {
|
||||
newTaints := []api.Taint{}
|
||||
found := false
|
||||
for _, taint := range taints {
|
||||
if taint.Key == key {
|
||||
found = true
|
||||
continue
|
||||
}
|
||||
newTaints = append(newTaints, taint)
|
||||
}
|
||||
|
||||
if !found {
|
||||
return nil, fmt.Errorf("taint key=\"%s\" not found.", key)
|
||||
}
|
||||
return newTaints, nil
|
||||
}
|
||||
|
||||
// reorganizeTaints returns the updated set of taints, taking into account old taints that were not updated,
|
||||
// old taints that were updated, old taints that were deleted, and new taints.
|
||||
func reorganizeTaints(accessor meta.Object, overwrite bool, taintsToAdd []api.Taint, removeKeys []string) ([]api.Taint, error) {
|
||||
newTaints := append([]api.Taint{}, taintsToAdd...)
|
||||
|
||||
var oldTaints []api.Taint
|
||||
var err error
|
||||
annotations := accessor.GetAnnotations()
|
||||
if annotations != nil {
|
||||
if oldTaints, err = api.GetTaintsFromNodeAnnotations(annotations); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// add taints that already existing but not updated to newTaints
|
||||
for _, oldTaint := range oldTaints {
|
||||
existsInNew := false
|
||||
for _, taint := range newTaints {
|
||||
if taint.Key == oldTaint.Key {
|
||||
existsInNew = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !existsInNew {
|
||||
newTaints = append(newTaints, oldTaint)
|
||||
}
|
||||
}
|
||||
|
||||
allErrs := []error{}
|
||||
for _, taintToRemove := range removeKeys {
|
||||
newTaints, err = deleteTaintByKey(newTaints, taintToRemove)
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, err)
|
||||
}
|
||||
}
|
||||
return newTaints, utilerrors.NewAggregate(allErrs)
|
||||
}
|
||||
|
||||
func parseTaints(spec []string) ([]api.Taint, []string, error) {
|
||||
var taints []api.Taint
|
||||
var remove []string
|
||||
for _, taintSpec := range spec {
|
||||
if strings.Index(taintSpec, "=") != -1 && strings.Index(taintSpec, ":") != -1 {
|
||||
parts := strings.Split(taintSpec, "=")
|
||||
if len(parts) != 2 || len(parts[1]) == 0 || len(validation.IsQualifiedName(parts[0])) > 0 {
|
||||
return nil, nil, fmt.Errorf("invalid taint spec: %v", taintSpec)
|
||||
}
|
||||
|
||||
parts2 := strings.Split(parts[1], ":")
|
||||
errs := validation.IsValidLabelValue(parts2[0])
|
||||
if len(parts2) != 2 || len(errs) != 0 {
|
||||
return nil, nil, fmt.Errorf("invalid taint spec: %v, %s", taintSpec, strings.Join(errs, "; "))
|
||||
}
|
||||
|
||||
if parts2[1] != string(api.TaintEffectNoSchedule) && parts2[1] != string(api.TaintEffectPreferNoSchedule) {
|
||||
return nil, nil, fmt.Errorf("invalid taint spec: %v, unsupported taint effect", taintSpec)
|
||||
}
|
||||
|
||||
effect := api.TaintEffect(parts2[1])
|
||||
newTaint := api.Taint{
|
||||
Key: parts[0],
|
||||
Value: parts2[0],
|
||||
Effect: effect,
|
||||
}
|
||||
|
||||
taints = append(taints, newTaint)
|
||||
} else if strings.HasSuffix(taintSpec, "-") {
|
||||
remove = append(remove, taintSpec[:len(taintSpec)-1])
|
||||
} else {
|
||||
return nil, nil, fmt.Errorf("unknown taint spec: %v", taintSpec)
|
||||
}
|
||||
}
|
||||
return taints, remove, nil
|
||||
}
|
||||
|
||||
// Complete adapts from the command line args and factory to the data required.
|
||||
func (o *TaintOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) (err error) {
|
||||
namespace, _, err := f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// retrieves resource and taint args from args
|
||||
// also checks args to verify that all resources are specified before taints
|
||||
taintArgs := []string{}
|
||||
metTaintArg := false
|
||||
for _, s := range args {
|
||||
isTaint := strings.Contains(s, "=") || strings.HasSuffix(s, "-")
|
||||
switch {
|
||||
case !metTaintArg && isTaint:
|
||||
metTaintArg = true
|
||||
fallthrough
|
||||
case metTaintArg && isTaint:
|
||||
taintArgs = append(taintArgs, s)
|
||||
case !metTaintArg && !isTaint:
|
||||
o.resources = append(o.resources, s)
|
||||
case metTaintArg && !isTaint:
|
||||
return fmt.Errorf("all resources must be specified before taint changes: %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
if len(o.resources) < 1 {
|
||||
return fmt.Errorf("one or more resources must be specified as <resource> <name>")
|
||||
}
|
||||
if len(taintArgs) < 1 {
|
||||
return fmt.Errorf("at least one taint update is required")
|
||||
}
|
||||
|
||||
if o.taintsToAdd, o.removeTaintKeys, err = parseTaints(taintArgs); err != nil {
|
||||
return cmdutil.UsageError(cmd, err.Error())
|
||||
}
|
||||
|
||||
mapper, typer := f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd))
|
||||
o.builder = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
|
||||
ContinueOnError().
|
||||
NamespaceParam(namespace).DefaultNamespace()
|
||||
if o.all {
|
||||
o.builder = o.builder.SelectAllParam(o.all).ResourceTypes("node")
|
||||
} else {
|
||||
if len(o.resources) < 2 {
|
||||
return fmt.Errorf("at least one resource name must be specified since 'all' parameter is not set")
|
||||
}
|
||||
o.builder = o.builder.ResourceNames("node", o.resources[1:]...)
|
||||
}
|
||||
o.builder = o.builder.SelectorParam(o.selector).
|
||||
Flatten().
|
||||
Latest()
|
||||
|
||||
o.f = f
|
||||
o.out = out
|
||||
o.cmd = cmd
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate checks to the TaintOptions to see if there is sufficient information run the command.
|
||||
func (o TaintOptions) Validate(args []string) error {
|
||||
resourceType := strings.ToLower(o.resources[0])
|
||||
if resourceType != "node" && resourceType != "nodes" {
|
||||
return fmt.Errorf("invalid resource type %s, only node(s) is supported", o.resources[0])
|
||||
}
|
||||
|
||||
// check the format of taint args and checks removed taints aren't in the new taints list
|
||||
conflictKeys := []string{}
|
||||
removeTaintKeysSet := sets.NewString(o.removeTaintKeys...)
|
||||
for _, taint := range o.taintsToAdd {
|
||||
if removeTaintKeysSet.Has(taint.Key) {
|
||||
conflictKeys = append(conflictKeys, taint.Key)
|
||||
}
|
||||
}
|
||||
if len(conflictKeys) > 0 {
|
||||
return fmt.Errorf("can not both modify and remove the following taint(s) in the same command: %s", strings.Join(conflictKeys, ", "))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunTaint does the work
|
||||
func (o TaintOptions) RunTaint() error {
|
||||
r := o.builder.Do()
|
||||
if err := r.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obj, err := info.Mapping.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name, namespace := info.Name, info.Namespace
|
||||
oldData, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := o.updateTaints(obj); err != nil {
|
||||
return err
|
||||
}
|
||||
newData, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, obj)
|
||||
createdPatch := err == nil
|
||||
if err != nil {
|
||||
glog.V(2).Infof("couldn't compute patch: %v", err)
|
||||
}
|
||||
|
||||
mapping := info.ResourceMapping()
|
||||
client, err := o.f.ClientForMapping(mapping)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
helper := resource.NewHelper(client, mapping)
|
||||
|
||||
var outputObj runtime.Object
|
||||
if createdPatch {
|
||||
outputObj, err = helper.Patch(namespace, name, api.StrategicMergePatchType, patchBytes)
|
||||
} else {
|
||||
outputObj, err = helper.Replace(namespace, name, false, obj)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mapper, _ := o.f.Object(cmdutil.GetIncludeThirdPartyAPIs(o.cmd))
|
||||
outputFormat := cmdutil.GetFlagString(o.cmd, "output")
|
||||
if outputFormat != "" {
|
||||
return o.f.PrintObject(o.cmd, mapper, outputObj, o.out)
|
||||
}
|
||||
|
||||
cmdutil.PrintSuccess(mapper, false, o.out, info.Mapping.Resource, info.Name, "tainted")
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// validateNoTaintOverwrites validates that when overwrite is false, to-be-updated taints don't exist in the node taint list (yet)
|
||||
func validateNoTaintOverwrites(accessor meta.Object, taints []api.Taint) error {
|
||||
annotations := accessor.GetAnnotations()
|
||||
if annotations == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
allErrs := []error{}
|
||||
oldTaints, err := api.GetTaintsFromNodeAnnotations(annotations)
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, err)
|
||||
return utilerrors.NewAggregate(allErrs)
|
||||
}
|
||||
|
||||
for _, taint := range taints {
|
||||
for _, oldTaint := range oldTaints {
|
||||
if taint.Key == oldTaint.Key {
|
||||
allErrs = append(allErrs, fmt.Errorf("Node '%s' already has a taint (%+v), and --overwrite is false", accessor.GetName(), taint))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return utilerrors.NewAggregate(allErrs)
|
||||
}
|
||||
|
||||
// updateTaints updates taints of obj
|
||||
func (o TaintOptions) updateTaints(obj runtime.Object) error {
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !o.overwrite {
|
||||
if err := validateNoTaintOverwrites(accessor, o.taintsToAdd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
annotations := accessor.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
newTaints, err := reorganizeTaints(accessor, o.overwrite, o.taintsToAdd, o.removeTaintKeys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
taintsData, err := json.Marshal(newTaints)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
annotations[api.TaintsAnnotationKey] = string(taintsData)
|
||||
accessor.SetAnnotations(annotations)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/fake"
|
||||
"k8s.io/kubernetes/pkg/conversion"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
func generateNodeAndTaintedNode(oldTaints []api.Taint, newTaints []api.Taint) (*api.Node, *api.Node) {
|
||||
var taintedNode *api.Node
|
||||
|
||||
oldTaintsData, _ := json.Marshal(oldTaints)
|
||||
// Create a node.
|
||||
node := &api.Node{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "node-name",
|
||||
CreationTimestamp: unversioned.Time{Time: time.Now()},
|
||||
Annotations: map[string]string{
|
||||
api.TaintsAnnotationKey: string(oldTaintsData),
|
||||
},
|
||||
},
|
||||
Spec: api.NodeSpec{
|
||||
ExternalID: "node-name",
|
||||
},
|
||||
Status: api.NodeStatus{},
|
||||
}
|
||||
clone, _ := conversion.NewCloner().DeepCopy(node)
|
||||
|
||||
newTaintsData, _ := json.Marshal(newTaints)
|
||||
// A copy of the same node, but tainted.
|
||||
taintedNode = clone.(*api.Node)
|
||||
taintedNode.Annotations = map[string]string{
|
||||
api.TaintsAnnotationKey: string(newTaintsData),
|
||||
}
|
||||
|
||||
return node, taintedNode
|
||||
}
|
||||
|
||||
func AnnotationsHaveEqualTaints(annotationA map[string]string, annotationB map[string]string) bool {
|
||||
taintsA, err := api.GetTaintsFromNodeAnnotations(annotationA)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
taintsB, err := api.GetTaintsFromNodeAnnotations(annotationB)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(taintsA) != len(taintsB) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, taintA := range taintsA {
|
||||
found := false
|
||||
for _, taintB := range taintsB {
|
||||
if reflect.DeepEqual(taintA, taintB) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestTaint(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
oldTaints []api.Taint
|
||||
newTaints []api.Taint
|
||||
args []string
|
||||
expectFatal bool
|
||||
expectTaint bool
|
||||
}{
|
||||
// success cases
|
||||
{
|
||||
description: "taints a node with effect NoSchedule",
|
||||
newTaints: []api.Taint{{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: "NoSchedule",
|
||||
}},
|
||||
args: []string{"node", "node-name", "foo=bar:NoSchedule"},
|
||||
expectFatal: false,
|
||||
expectTaint: true,
|
||||
},
|
||||
{
|
||||
description: "taints a node with effect PreferNoSchedule",
|
||||
newTaints: []api.Taint{{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: "PreferNoSchedule",
|
||||
}},
|
||||
args: []string{"node", "node-name", "foo=bar:PreferNoSchedule"},
|
||||
expectFatal: false,
|
||||
expectTaint: true,
|
||||
},
|
||||
{
|
||||
description: "update an existing taint on the node, change the effect from NoSchedule to PreferNoSchedule",
|
||||
oldTaints: []api.Taint{{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: "NoSchedule",
|
||||
}},
|
||||
newTaints: []api.Taint{{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: "PreferNoSchedule",
|
||||
}},
|
||||
args: []string{"node", "node-name", "foo=bar:PreferNoSchedule", "--overwrite"},
|
||||
expectFatal: false,
|
||||
expectTaint: true,
|
||||
},
|
||||
{
|
||||
description: "taints a node with two taints",
|
||||
newTaints: []api.Taint{{
|
||||
Key: "dedicated",
|
||||
Value: "namespaceA",
|
||||
Effect: "NoSchedule",
|
||||
}, {
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: "PreferNoSchedule",
|
||||
}},
|
||||
args: []string{"node", "node-name", "dedicated=namespaceA:NoSchedule", "foo=bar:PreferNoSchedule"},
|
||||
expectFatal: false,
|
||||
expectTaint: true,
|
||||
},
|
||||
{
|
||||
description: "node has two taints, remove one of them",
|
||||
oldTaints: []api.Taint{{
|
||||
Key: "dedicated",
|
||||
Value: "namespaceA",
|
||||
Effect: "NoSchedule",
|
||||
}, {
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: "PreferNoSchedule",
|
||||
}},
|
||||
newTaints: []api.Taint{{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: "PreferNoSchedule",
|
||||
}},
|
||||
args: []string{"node", "node-name", "dedicated-"},
|
||||
expectFatal: false,
|
||||
expectTaint: true,
|
||||
},
|
||||
{
|
||||
description: "node has two taints, update one of them and remove the other",
|
||||
oldTaints: []api.Taint{{
|
||||
Key: "dedicated",
|
||||
Value: "namespaceA",
|
||||
Effect: "NoSchedule",
|
||||
}, {
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: "PreferNoSchedule",
|
||||
}},
|
||||
newTaints: []api.Taint{{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: "NoSchedule",
|
||||
}},
|
||||
args: []string{"node", "node-name", "dedicated-", "foo=bar:NoSchedule", "--overwrite"},
|
||||
expectFatal: false,
|
||||
expectTaint: true,
|
||||
},
|
||||
|
||||
// error cases
|
||||
{
|
||||
description: "invalid taint key",
|
||||
args: []string{"node", "node-name", "nospecialchars^@=banana:NoSchedule"},
|
||||
expectFatal: true,
|
||||
expectTaint: false,
|
||||
},
|
||||
{
|
||||
description: "invalid taint effect",
|
||||
args: []string{"node", "node-name", "foo=bar:NoExcute"},
|
||||
expectFatal: true,
|
||||
expectTaint: false,
|
||||
},
|
||||
{
|
||||
description: "can't update existing taint on the node, since 'overwrite' flag is not set",
|
||||
oldTaints: []api.Taint{{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: "NoSchedule",
|
||||
}},
|
||||
newTaints: []api.Taint{{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: "NoSchedule",
|
||||
}},
|
||||
args: []string{"node", "node-name", "foo=bar:PreferNoSchedule"},
|
||||
expectFatal: true,
|
||||
expectTaint: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
oldNode, expectNewNode := generateNodeAndTaintedNode(test.oldTaints, test.newTaints)
|
||||
|
||||
new_node := &api.Node{}
|
||||
tainted := false
|
||||
f, tf, codec := NewAPIFactory()
|
||||
|
||||
tf.Client = &fake.RESTClient{
|
||||
Codec: codec,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
m := &MyReq{req}
|
||||
switch {
|
||||
case m.isFor("GET", "/nodes/node-name"):
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, oldNode)}, nil
|
||||
case m.isFor("PATCH", "/nodes/node-name"), m.isFor("PUT", "/nodes/node-name"):
|
||||
tainted = true
|
||||
data, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: unexpected error: %v", test.description, err)
|
||||
}
|
||||
defer req.Body.Close()
|
||||
if err := runtime.DecodeInto(codec, data, new_node); err != nil {
|
||||
t.Fatalf("%s: unexpected error: %v", test.description, err)
|
||||
}
|
||||
if !AnnotationsHaveEqualTaints(expectNewNode.Annotations, new_node.Annotations) {
|
||||
t.Fatalf("%s: expected:\n%v\nsaw:\n%v\n", test.description, expectNewNode.Annotations, new_node.Annotations)
|
||||
}
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, new_node)}, nil
|
||||
default:
|
||||
t.Fatalf("%s: unexpected request: %v %#v\n%#v", test.description, req.Method, req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.ClientConfig = defaultClientConfig()
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdTaint(f, buf)
|
||||
|
||||
saw_fatal := false
|
||||
func() {
|
||||
defer func() {
|
||||
// Recover from the panic below.
|
||||
_ = recover()
|
||||
// Restore cmdutil behavior
|
||||
cmdutil.DefaultBehaviorOnFatal()
|
||||
}()
|
||||
cmdutil.BehaviorOnFatal(func(e string) { saw_fatal = true; panic(e) })
|
||||
cmd.SetArgs(test.args)
|
||||
cmd.Execute()
|
||||
}()
|
||||
|
||||
if test.expectFatal {
|
||||
if !saw_fatal {
|
||||
t.Fatalf("%s: unexpected non-error", test.description)
|
||||
}
|
||||
}
|
||||
|
||||
if test.expectTaint {
|
||||
if !tainted {
|
||||
t.Fatalf("%s: node not tainted", test.description)
|
||||
}
|
||||
}
|
||||
if !test.expectTaint {
|
||||
if tainted {
|
||||
t.Fatalf("%s: unexpected taint", test.description)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1617,6 +1617,7 @@ func describeNode(node *api.Node, nodeNonTerminatedPodsList *api.PodList, events
|
|||
return tabbedString(func(out io.Writer) error {
|
||||
fmt.Fprintf(out, "Name:\t%s\n", node.Name)
|
||||
printLabelsMultiline(out, "Labels", node.Labels)
|
||||
printTaintsInAnnotationMultiline(out, "Taints", node.Annotations)
|
||||
fmt.Fprintf(out, "CreationTimestamp:\t%s\n", node.CreationTimestamp.Time.Format(time.RFC1123Z))
|
||||
fmt.Fprintf(out, "Phase:\t%v\n", node.Status.Phase)
|
||||
if len(node.Status.Conditions) > 0 {
|
||||
|
@ -2256,3 +2257,42 @@ func printLabelsMultilineWithIndent(out io.Writer, initialIndent, title, innerIn
|
|||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// printTaintsMultiline prints multiple taints with a proper alignment.
|
||||
func printTaintsInAnnotationMultiline(out io.Writer, title string, annotations map[string]string) {
|
||||
taints, err := api.GetTaintsFromNodeAnnotations(annotations)
|
||||
if err != nil {
|
||||
taints = []api.Taint{}
|
||||
}
|
||||
printTaintsMultilineWithIndent(out, "", title, "\t", taints)
|
||||
}
|
||||
|
||||
// printTaintsMultilineWithIndent prints multiple taints with a user-defined alignment.
|
||||
func printTaintsMultilineWithIndent(out io.Writer, initialIndent, title, innerIndent string, taints []api.Taint) {
|
||||
fmt.Fprintf(out, "%s%s:%s", initialIndent, title, innerIndent)
|
||||
|
||||
if taints == nil || len(taints) == 0 {
|
||||
fmt.Fprintln(out, "<none>")
|
||||
return
|
||||
}
|
||||
|
||||
// to print taints in the sorted order
|
||||
keys := make([]string, 0, len(taints))
|
||||
for _, taint := range taints {
|
||||
keys = append(keys, taint.Key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for i, key := range keys {
|
||||
for _, taint := range taints {
|
||||
if taint.Key == key {
|
||||
if i != 0 {
|
||||
fmt.Fprint(out, initialIndent)
|
||||
fmt.Fprint(out, innerIndent)
|
||||
}
|
||||
fmt.Fprintf(out, "%s=%s:%s\n", taint.Key, taint.Value, taint.Effect)
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ var (
|
|||
ErrVolumeZoneConflict = newPredicateFailureError("NoVolumeZoneConflict")
|
||||
ErrNodeSelectorNotMatch = newPredicateFailureError("MatchNodeSelector")
|
||||
ErrPodAffinityNotMatch = newPredicateFailureError("MatchInterPodAffinity")
|
||||
ErrTaintsTolerationsNotMatch = newPredicateFailureError("PodToleratesNodeTaints")
|
||||
ErrPodNotMatchHostName = newPredicateFailureError("HostName")
|
||||
ErrPodNotFitsHostPorts = newPredicateFailureError("PodFitsHostPorts")
|
||||
ErrNodeLabelPresenceViolated = newPredicateFailureError("CheckNodeLabelPresence")
|
||||
|
|
|
@ -944,3 +944,58 @@ func (checker *PodAffinityChecker) NodeMatchPodAffinityAntiAffinity(pod *api.Pod
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type TolerationMatch struct {
|
||||
info NodeInfo
|
||||
}
|
||||
|
||||
func NewTolerationMatchPredicate(info NodeInfo) algorithm.FitPredicate {
|
||||
tolerationMatch := &TolerationMatch{
|
||||
info: info,
|
||||
}
|
||||
return tolerationMatch.PodToleratesNodeTaints
|
||||
}
|
||||
|
||||
func (t *TolerationMatch) PodToleratesNodeTaints(pod *api.Pod, nodeInfo *schedulercache.NodeInfo) (bool, error) {
|
||||
node := nodeInfo.Node()
|
||||
|
||||
taints, err := api.GetTaintsFromNodeAnnotations(node.Annotations)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
tolerations, err := api.GetTolerationsFromPodAnnotations(pod.Annotations)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if tolerationsToleratesTaints(tolerations, taints) {
|
||||
return true, nil
|
||||
}
|
||||
return false, ErrTaintsTolerationsNotMatch
|
||||
}
|
||||
|
||||
func tolerationsToleratesTaints(tolerations []api.Toleration, taints []api.Taint) bool {
|
||||
// If the taint list is nil/empty, it is tolerated by all tolerations by default.
|
||||
if len(taints) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// The taint list isn't nil/empty, a nil/empty toleration list can't tolerate them.
|
||||
if len(tolerations) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, taint := range taints {
|
||||
// skip taints that have effect PreferNoSchedule, since it is for priorities
|
||||
if taint.Effect == api.TaintEffectPreferNoSchedule {
|
||||
continue
|
||||
}
|
||||
|
||||
if !api.TaintToleratedByTolerations(taint, tolerations) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -2358,3 +2358,286 @@ func TestInterPodAffinityWithMultipleNodes(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPodToleratesTaints(t *testing.T) {
|
||||
podTolerateTaintsTests := []struct {
|
||||
pod *api.Pod
|
||||
node api.Node
|
||||
fits bool
|
||||
test string
|
||||
}{
|
||||
{
|
||||
pod: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "pod0",
|
||||
},
|
||||
},
|
||||
node: api.Node{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
api.TaintsAnnotationKey: `
|
||||
[{
|
||||
"key": "dedicated",
|
||||
"value": "user1",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
},
|
||||
fits: false,
|
||||
test: "a pod having no tolerations can't be scheduled onto a node with nonempty taints",
|
||||
},
|
||||
{
|
||||
pod: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "pod1",
|
||||
Annotations: map[string]string{
|
||||
api.TolerationsAnnotationKey: `
|
||||
[{
|
||||
"key": "dedicated",
|
||||
"value": "user1",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{Image: "pod1:V1"}},
|
||||
},
|
||||
},
|
||||
node: api.Node{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
api.TaintsAnnotationKey: `
|
||||
[{
|
||||
"key": "dedicated",
|
||||
"value": "user1",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
},
|
||||
fits: true,
|
||||
test: "a pod which can be scheduled on a dedicated node assgined to user1 with effect NoSchedule",
|
||||
},
|
||||
{
|
||||
pod: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "pod2",
|
||||
Annotations: map[string]string{
|
||||
api.TolerationsAnnotationKey: `
|
||||
[{
|
||||
"key": "dedicated",
|
||||
"operator": "Equal",
|
||||
"value": "user2",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{Image: "pod2:V1"}},
|
||||
},
|
||||
},
|
||||
node: api.Node{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
api.TaintsAnnotationKey: `
|
||||
[{
|
||||
"key": "dedicated",
|
||||
"value": "user1",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
},
|
||||
fits: false,
|
||||
test: "a pod which can't be scheduled on a dedicated node assgined to user2 with effect NoSchedule",
|
||||
},
|
||||
{
|
||||
pod: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "pod2",
|
||||
Annotations: map[string]string{
|
||||
api.TolerationsAnnotationKey: `
|
||||
[{
|
||||
"key": "foo",
|
||||
"operator": "Exists",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{Image: "pod2:V1"}},
|
||||
},
|
||||
},
|
||||
node: api.Node{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
api.TaintsAnnotationKey: `
|
||||
[{
|
||||
"key": "foo",
|
||||
"value": "bar",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
},
|
||||
fits: true,
|
||||
test: "a pod can be scheduled onto the node, with a toleration uses operator Exists that tolerates the taints on the node",
|
||||
},
|
||||
{
|
||||
pod: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "pod2",
|
||||
Annotations: map[string]string{
|
||||
api.TolerationsAnnotationKey: `
|
||||
[{
|
||||
"key": "dedicated",
|
||||
"operator": "Equal",
|
||||
"value": "user2",
|
||||
"effect": "NoSchedule"
|
||||
}, {
|
||||
"key": "foo",
|
||||
"operator": "Exists",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{Image: "pod2:V1"}},
|
||||
},
|
||||
},
|
||||
node: api.Node{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
api.TaintsAnnotationKey: `
|
||||
[{
|
||||
"key": "dedicated",
|
||||
"value": "user2",
|
||||
"effect": "NoSchedule"
|
||||
}, {
|
||||
"key": "foo",
|
||||
"value": "bar",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
},
|
||||
fits: true,
|
||||
test: "a pod has multiple tolerations, node has multiple taints, all the taints are tolerated, pod can be scheduled onto the node",
|
||||
},
|
||||
{
|
||||
pod: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "pod2",
|
||||
Annotations: map[string]string{
|
||||
api.TolerationsAnnotationKey: `
|
||||
[{
|
||||
"key": "foo",
|
||||
"operator": "Equal",
|
||||
"value": "bar",
|
||||
"effect": "PreferNoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{Image: "pod2:V1"}},
|
||||
},
|
||||
},
|
||||
node: api.Node{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
api.TaintsAnnotationKey: `
|
||||
[{
|
||||
"key": "foo",
|
||||
"value": "bar",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
},
|
||||
fits: false,
|
||||
test: "a pod has a toleration that keys and values match the taint on the node, but (non-empty) effect doesn't match, " +
|
||||
"can't be scheduled onto the node",
|
||||
},
|
||||
{
|
||||
pod: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "pod2",
|
||||
Annotations: map[string]string{
|
||||
api.TolerationsAnnotationKey: `
|
||||
[{
|
||||
"key": "foo",
|
||||
"operator": "Equal",
|
||||
"value": "bar"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{Image: "pod2:V1"}},
|
||||
},
|
||||
},
|
||||
node: api.Node{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
api.TaintsAnnotationKey: `
|
||||
[{
|
||||
"key": "foo",
|
||||
"value": "bar",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
},
|
||||
fits: true,
|
||||
test: "The pod has a toleration that keys and values match the taint on the node, the effect of toleration is empty, " +
|
||||
"and the effect of taint is NoSchedule. Pod can be scheduled onto the node",
|
||||
},
|
||||
{
|
||||
pod: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "pod2",
|
||||
Annotations: map[string]string{
|
||||
api.TolerationsAnnotationKey: `
|
||||
[{
|
||||
"key": "dedicated",
|
||||
"operator": "Equal",
|
||||
"value": "user2",
|
||||
"effect": "NoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{Image: "pod2:V1"}},
|
||||
},
|
||||
},
|
||||
node: api.Node{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
api.TaintsAnnotationKey: `
|
||||
[{
|
||||
"key": "dedicated",
|
||||
"value": "user1",
|
||||
"effect": "PreferNoSchedule"
|
||||
}]`,
|
||||
},
|
||||
},
|
||||
},
|
||||
fits: true,
|
||||
test: "The pod has a toleration that key and value don't match the taint on the node, " +
|
||||
"but the effect of taint on node is PreferNochedule. Pod can be shceduled onto the node",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range podTolerateTaintsTests {
|
||||
tolerationMatch := TolerationMatch{FakeNodeInfo(test.node)}
|
||||
nodeInfo := schedulercache.NewNodeInfo()
|
||||
nodeInfo.SetNode(&test.node)
|
||||
fits, err := tolerationMatch.PodToleratesNodeTaints(test.pod, nodeInfo)
|
||||
if fits == false && !reflect.DeepEqual(err, ErrTaintsTolerationsNotMatch) {
|
||||
t.Errorf("%s, unexpected error: %v", test.test, err)
|
||||
}
|
||||
if fits != test.fits {
|
||||
t.Errorf("%s, expected: %v got %v", test.test, test.fits, fits)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package priorities
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm"
|
||||
schedulerapi "k8s.io/kubernetes/plugin/pkg/scheduler/api"
|
||||
"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"
|
||||
)
|
||||
|
||||
// NodeTaints hold the node lister
|
||||
type TaintToleration struct {
|
||||
nodeLister algorithm.NodeLister
|
||||
}
|
||||
|
||||
// NewTaintTolerationPriority
|
||||
func NewTaintTolerationPriority(nodeLister algorithm.NodeLister) algorithm.PriorityFunction {
|
||||
taintToleration := &TaintToleration{
|
||||
nodeLister: nodeLister,
|
||||
}
|
||||
return taintToleration.ComputeTaintTolerationPriority
|
||||
}
|
||||
|
||||
// CountIntolerableTaintsPreferNoSchedule gives the count of intolerable taints of a pod with effect PreferNoSchedule
|
||||
func countIntolerableTaintsPreferNoSchedule(taints []api.Taint, tolerations []api.Toleration) (intolerableTaints int) {
|
||||
for _, taint := range taints {
|
||||
// check only on taints that have effect PreferNoSchedule
|
||||
if taint.Effect != api.TaintEffectPreferNoSchedule {
|
||||
continue
|
||||
}
|
||||
|
||||
if !api.TaintToleratedByTolerations(taint, tolerations) {
|
||||
intolerableTaints++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getAllTolerationEffectPreferNoSchedule gets the list of all Toleration with Effect PreferNoSchedule
|
||||
func getAllTolerationPreferNoSchedule(tolerations []api.Toleration) (tolerationList []api.Toleration) {
|
||||
for _, toleration := range tolerations {
|
||||
if len(toleration.Effect) == 0 || toleration.Effect == api.TaintEffectPreferNoSchedule {
|
||||
tolerationList = append(tolerationList, toleration)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ComputeTaintTolerationPriority prepares the priority list for all the nodes based on the number of intolerable taints on the node
|
||||
func (s *TaintToleration) ComputeTaintTolerationPriority(pod *api.Pod, nodeNameToInfo map[string]*schedulercache.NodeInfo, nodeLister algorithm.NodeLister) (schedulerapi.HostPriorityList, error) {
|
||||
// counts hold the count of intolerable taints of a pod for a given node
|
||||
counts := make(map[string]int)
|
||||
|
||||
// the max value of counts
|
||||
var maxCount int
|
||||
|
||||
nodes, err := nodeLister.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tolerations, err := api.GetTolerationsFromPodAnnotations(pod.Annotations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Fetch a list of all toleration with effect PreferNoSchedule
|
||||
tolerationList := getAllTolerationPreferNoSchedule(tolerations)
|
||||
|
||||
// calculate the intolerable taints for all the nodes
|
||||
for _, node := range nodes.Items {
|
||||
taints, err := api.GetTaintsFromNodeAnnotations(node.Annotations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
count := countIntolerableTaintsPreferNoSchedule(taints, tolerationList)
|
||||
counts[node.Name] = count
|
||||
if count > maxCount {
|
||||
maxCount = count
|
||||
}
|
||||
}
|
||||
|
||||
// The maximum priority value to give to a node
|
||||
// Priority values range from 0 - maxPriority
|
||||
const maxPriority = 10
|
||||
result := []schedulerapi.HostPriority{}
|
||||
for _, node := range nodes.Items {
|
||||
fScore := float64(maxPriority)
|
||||
if maxCount > 0 {
|
||||
fScore = (1.0 - float64(counts[node.Name])/float64(maxCount)) * 10
|
||||
}
|
||||
result = append(result, schedulerapi.HostPriority{Host: node.Name, Score: int(fScore)})
|
||||
}
|
||||
return result, nil
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package priorities
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm"
|
||||
schedulerapi "k8s.io/kubernetes/plugin/pkg/scheduler/api"
|
||||
"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"
|
||||
)
|
||||
|
||||
func nodeWithTaints(nodeName string, taints []api.Taint) api.Node {
|
||||
taintsData, _ := json.Marshal(taints)
|
||||
return api.Node{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: nodeName,
|
||||
Annotations: map[string]string{
|
||||
api.TaintsAnnotationKey: string(taintsData),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func podWithTolerations(tolerations []api.Toleration) *api.Pod {
|
||||
tolerationData, _ := json.Marshal(tolerations)
|
||||
return &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
api.TolerationsAnnotationKey: string(tolerationData),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// This function will create a set of nodes and pods and test the priority
|
||||
// Nodes with zero,one,two,three,four and hundred taints are created
|
||||
// Pods with zero,one,two,three,four and hundred tolerations are created
|
||||
|
||||
func TestTaintAndToleration(t *testing.T) {
|
||||
tests := []struct {
|
||||
pod *api.Pod
|
||||
nodes []api.Node
|
||||
expectedList schedulerapi.HostPriorityList
|
||||
test string
|
||||
}{
|
||||
// basic test case
|
||||
{
|
||||
test: "node with taints tolerated by the pod, gets a higher score than those node with intolerable taints",
|
||||
pod: podWithTolerations([]api.Toleration{{
|
||||
Key: "foo",
|
||||
Operator: api.TolerationOpEqual,
|
||||
Value: "bar",
|
||||
Effect: api.TaintEffectPreferNoSchedule,
|
||||
}}),
|
||||
nodes: []api.Node{
|
||||
nodeWithTaints("nodeA", []api.Taint{{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: api.TaintEffectPreferNoSchedule,
|
||||
}}),
|
||||
nodeWithTaints("nodeB", []api.Taint{{
|
||||
Key: "foo",
|
||||
Value: "blah",
|
||||
Effect: api.TaintEffectPreferNoSchedule,
|
||||
}}),
|
||||
},
|
||||
expectedList: []schedulerapi.HostPriority{
|
||||
{Host: "nodeA", Score: 10},
|
||||
{Host: "nodeB", Score: 0},
|
||||
},
|
||||
},
|
||||
// the count of taints that are tolerated by pod, does not matter.
|
||||
{
|
||||
test: "the nodes that all of their taints are tolerated by the pod, get the same score, no matter how many tolerable taints a node has",
|
||||
pod: podWithTolerations([]api.Toleration{
|
||||
{
|
||||
Key: "cpu-type",
|
||||
Operator: api.TolerationOpEqual,
|
||||
Value: "arm64",
|
||||
Effect: api.TaintEffectPreferNoSchedule,
|
||||
}, {
|
||||
Key: "disk-type",
|
||||
Operator: api.TolerationOpEqual,
|
||||
Value: "ssd",
|
||||
Effect: api.TaintEffectPreferNoSchedule,
|
||||
},
|
||||
}),
|
||||
nodes: []api.Node{
|
||||
nodeWithTaints("nodeA", []api.Taint{}),
|
||||
nodeWithTaints("nodeB", []api.Taint{
|
||||
{
|
||||
Key: "cpu-type",
|
||||
Value: "arm64",
|
||||
Effect: api.TaintEffectPreferNoSchedule,
|
||||
},
|
||||
}),
|
||||
nodeWithTaints("nodeC", []api.Taint{
|
||||
{
|
||||
Key: "cpu-type",
|
||||
Value: "arm64",
|
||||
Effect: api.TaintEffectPreferNoSchedule,
|
||||
}, {
|
||||
Key: "disk-type",
|
||||
Value: "ssd",
|
||||
Effect: api.TaintEffectPreferNoSchedule,
|
||||
},
|
||||
}),
|
||||
},
|
||||
expectedList: []schedulerapi.HostPriority{
|
||||
{Host: "nodeA", Score: 10},
|
||||
{Host: "nodeB", Score: 10},
|
||||
{Host: "nodeC", Score: 10},
|
||||
},
|
||||
},
|
||||
// the count of taints on a node that are not tolerated by pod, matters.
|
||||
{
|
||||
test: "the more intolerable taints a node has, the lower score it gets.",
|
||||
pod: podWithTolerations([]api.Toleration{{
|
||||
Key: "foo",
|
||||
Operator: api.TolerationOpEqual,
|
||||
Value: "bar",
|
||||
Effect: api.TaintEffectPreferNoSchedule,
|
||||
}}),
|
||||
nodes: []api.Node{
|
||||
nodeWithTaints("nodeA", []api.Taint{}),
|
||||
nodeWithTaints("nodeB", []api.Taint{
|
||||
{
|
||||
Key: "cpu-type",
|
||||
Value: "arm64",
|
||||
Effect: api.TaintEffectPreferNoSchedule,
|
||||
},
|
||||
}),
|
||||
nodeWithTaints("nodeC", []api.Taint{
|
||||
{
|
||||
Key: "cpu-type",
|
||||
Value: "arm64",
|
||||
Effect: api.TaintEffectPreferNoSchedule,
|
||||
}, {
|
||||
Key: "disk-type",
|
||||
Value: "ssd",
|
||||
Effect: api.TaintEffectPreferNoSchedule,
|
||||
},
|
||||
}),
|
||||
},
|
||||
expectedList: []schedulerapi.HostPriority{
|
||||
{Host: "nodeA", Score: 10},
|
||||
{Host: "nodeB", Score: 5},
|
||||
{Host: "nodeC", Score: 0},
|
||||
},
|
||||
},
|
||||
// taints-tolerations priority only takes care about the taints and tolerations that have effect PreferNoSchedule
|
||||
{
|
||||
test: "only taints and tolerations that have effect PreferNoSchedule are checked by taints-tolerations priority function",
|
||||
pod: podWithTolerations([]api.Toleration{
|
||||
{
|
||||
Key: "cpu-type",
|
||||
Operator: api.TolerationOpEqual,
|
||||
Value: "arm64",
|
||||
Effect: api.TaintEffectNoSchedule,
|
||||
}, {
|
||||
Key: "disk-type",
|
||||
Operator: api.TolerationOpEqual,
|
||||
Value: "ssd",
|
||||
Effect: api.TaintEffectNoSchedule,
|
||||
},
|
||||
}),
|
||||
nodes: []api.Node{
|
||||
nodeWithTaints("nodeA", []api.Taint{}),
|
||||
nodeWithTaints("nodeB", []api.Taint{
|
||||
{
|
||||
Key: "cpu-type",
|
||||
Value: "arm64",
|
||||
Effect: api.TaintEffectNoSchedule,
|
||||
},
|
||||
}),
|
||||
nodeWithTaints("nodeC", []api.Taint{
|
||||
{
|
||||
Key: "cpu-type",
|
||||
Value: "arm64",
|
||||
Effect: api.TaintEffectPreferNoSchedule,
|
||||
}, {
|
||||
Key: "disk-type",
|
||||
Value: "ssd",
|
||||
Effect: api.TaintEffectPreferNoSchedule,
|
||||
},
|
||||
}),
|
||||
},
|
||||
expectedList: []schedulerapi.HostPriority{
|
||||
{Host: "nodeA", Score: 10},
|
||||
{Host: "nodeB", Score: 10},
|
||||
{Host: "nodeC", Score: 0},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
nodeNameToInfo := schedulercache.CreateNodeNameToInfoMap([]*api.Pod{{}})
|
||||
taintToleration := TaintToleration{nodeLister: algorithm.FakeNodeLister(api.NodeList{Items: test.nodes})}
|
||||
list, err := taintToleration.ComputeTaintTolerationPriority(
|
||||
test.pod,
|
||||
nodeNameToInfo,
|
||||
algorithm.FakeNodeLister(api.NodeList{Items: test.nodes}))
|
||||
if err != nil {
|
||||
t.Errorf("%s, unexpected error: %v", test.test, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(test.expectedList, list) {
|
||||
t.Errorf("%s,\nexpected:\n\t%+v,\ngot:\n\t%+v", test.test, test.expectedList, list)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -145,6 +145,14 @@ func defaultPredicates() sets.String {
|
|||
// GeneralPredicates are the predicates that are enforced by all Kubernetes components
|
||||
// (e.g. kubelet and all schedulers)
|
||||
factory.RegisterFitPredicate("GeneralPredicates", predicates.GeneralPredicates),
|
||||
|
||||
// Fit is determined based on whether a pod can tolerate all of the node's taints
|
||||
factory.RegisterFitPredicateFactory(
|
||||
"PodToleratesNodeTaints",
|
||||
func(args factory.PluginFactoryArgs) algorithm.FitPredicate {
|
||||
return predicates.NewTolerationMatchPredicate(args.NodeInfo)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -173,5 +181,14 @@ func defaultPriorities() sets.String {
|
|||
Weight: 1,
|
||||
},
|
||||
),
|
||||
factory.RegisterPriorityConfigFactory(
|
||||
"TaintTolerationPriority",
|
||||
factory.PriorityConfigFactory{
|
||||
Function: func(args factory.PluginFactoryArgs) algorithm.PriorityFunction {
|
||||
return priorities.NewTaintTolerationPriority(args.NodeLister)
|
||||
},
|
||||
Weight: 1,
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/controller"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
pkgutil "k8s.io/kubernetes/pkg/util"
|
||||
utilnet "k8s.io/kubernetes/pkg/util/net"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
|
@ -1283,6 +1284,39 @@ var _ = framework.KubeDescribe("Kubectl client", func() {
|
|||
}
|
||||
})
|
||||
})
|
||||
|
||||
framework.KubeDescribe("Kubectl taint", func() {
|
||||
It("should update the taint on a node", func() {
|
||||
taintName := fmt.Sprintf("kubernetes.io/e2e-taint-key-%s", string(pkgutil.NewUUID()))
|
||||
taintValue := "testing-taint-value"
|
||||
taintEffect := fmt.Sprintf("%s", api.TaintEffectNoSchedule)
|
||||
|
||||
nodes, err := c.Nodes().List(api.ListOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
node := nodes.Items[0]
|
||||
nodeName := node.Name
|
||||
|
||||
By("adding the taint " + taintName + " with value " + taintValue + " and taint effect " + taintEffect + " to a node")
|
||||
framework.RunKubectlOrDie("taint", "nodes", nodeName, taintName+"="+taintValue+":"+taintEffect)
|
||||
By("verifying the node has the taint " + taintName + " with the value " + taintValue)
|
||||
output := framework.RunKubectlOrDie("describe", "node", nodeName)
|
||||
requiredStrings := [][]string{
|
||||
{"Name:", nodeName},
|
||||
{"Taints:"},
|
||||
{taintName + "=" + taintValue + ":" + taintEffect},
|
||||
}
|
||||
checkOutput(output, requiredStrings)
|
||||
|
||||
By("removing the taint " + taintName + " of a node")
|
||||
framework.RunKubectlOrDie("taint", "nodes", nodeName, taintName+"-")
|
||||
By("verifying the node doesn't have the taint " + taintName)
|
||||
output = framework.RunKubectlOrDie("describe", "node", nodeName)
|
||||
if strings.Contains(output, taintName) {
|
||||
framework.Failf("Failed removing taint " + taintName + " of the node " + nodeName)
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Checks whether the output split by line contains the required elements.
|
||||
|
|
|
@ -19,6 +19,7 @@ package e2e
|
|||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
|
@ -1280,4 +1281,250 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() {
|
|||
framework.ExpectNoError(err)
|
||||
Expect(labelPod.Spec.NodeName).To(Equal(nodeName))
|
||||
})
|
||||
|
||||
// 1. Run a pod to get an available node, then delete the pod
|
||||
// 2. Taint the node with a random taint
|
||||
// 3. Try to relaunch the pod with tolerations tolerate the taints on node,
|
||||
// and the pod's nodeName specified to the name of node found in step 1
|
||||
It("validates that taints-tolerations is respected if matching", func() {
|
||||
// launch a pod to find a node which can launch a pod. We intentionally do
|
||||
// not just take the node list and choose the first of them. Depending on the
|
||||
// cluster and the scheduler it might be that a "normal" pod cannot be
|
||||
// scheduled onto it.
|
||||
By("Trying to launch a pod without a toleration to get a node which can launch it.")
|
||||
podName := "without-toleration"
|
||||
_, err := c.Pods(ns).Create(&api.Pod{
|
||||
TypeMeta: unversioned.TypeMeta{
|
||||
Kind: "Pod",
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: podName,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: podName,
|
||||
Image: "gcr.io/google_containers/pause-amd64:3.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectNoError(framework.WaitForPodRunningInNamespace(c, podName, ns))
|
||||
pod, err := c.Pods(ns).Get(podName)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
nodeName := pod.Spec.NodeName
|
||||
err = c.Pods(ns).Delete(podName, api.NewDeleteOptions(0))
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
By("Trying to apply a random taint on the found node.")
|
||||
taintName := fmt.Sprintf("kubernetes.io/e2e-taint-key-%s", string(util.NewUUID()))
|
||||
taintValue := "testing-taint-value"
|
||||
taintEffect := string(api.TaintEffectNoSchedule)
|
||||
framework.RunKubectlOrDie("taint", "nodes", nodeName, taintName+"="+taintValue+":"+taintEffect)
|
||||
By("verifying the node has the taint " + taintName + " with the value " + taintValue)
|
||||
output := framework.RunKubectlOrDie("describe", "node", nodeName)
|
||||
requiredStrings := [][]string{
|
||||
{"Name:", nodeName},
|
||||
{"Taints:"},
|
||||
{taintName, taintValue, taintEffect},
|
||||
}
|
||||
checkOutput(output, requiredStrings)
|
||||
|
||||
By("Trying to apply a random label on the found node.")
|
||||
labelKey := fmt.Sprintf("kubernetes.io/e2e-label-key-%s", string(util.NewUUID()))
|
||||
labelValue := "testing-label-value"
|
||||
framework.RunKubectlOrDie("label", "nodes", nodeName, labelKey+"="+labelValue)
|
||||
By("verifying the node has the label " + labelKey + " with the value " + labelValue)
|
||||
labelOutput := framework.RunKubectlOrDie("describe", "node", nodeName)
|
||||
labelOutputRequiredStrings := [][]string{
|
||||
{"Name:", nodeName},
|
||||
{"Labels:"},
|
||||
{labelKey + "=" + labelValue},
|
||||
}
|
||||
checkOutput(labelOutput, labelOutputRequiredStrings)
|
||||
|
||||
By("Trying to relaunch the pod, now with tolerations.")
|
||||
tolerationPodName := "with-tolerations"
|
||||
_, err = c.Pods(ns).Create(&api.Pod{
|
||||
TypeMeta: unversioned.TypeMeta{
|
||||
Kind: "Pod",
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: tolerationPodName,
|
||||
Annotations: map[string]string{
|
||||
"scheduler.alpha.kubernetes.io/tolerations": `
|
||||
[
|
||||
{
|
||||
"key": "` + taintName + `",
|
||||
"value": "` + taintValue + `",
|
||||
"effect": "` + taintEffect + `"
|
||||
}
|
||||
]`,
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
NodeSelector: map[string]string{labelKey: labelValue},
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: tolerationPodName,
|
||||
Image: "gcr.io/google_containers/pause-amd64:3.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
framework.ExpectNoError(err)
|
||||
defer c.Pods(ns).Delete(tolerationPodName, api.NewDeleteOptions(0))
|
||||
|
||||
// check that pod got scheduled. We intentionally DO NOT check that the
|
||||
// pod is running because this will create a race condition with the
|
||||
// kubelet and the scheduler: the scheduler might have scheduled a pod
|
||||
// already when the kubelet does not know about its new taint yet. The
|
||||
// kubelet will then refuse to launch the pod.
|
||||
framework.ExpectNoError(framework.WaitForPodNotPending(c, ns, tolerationPodName))
|
||||
deployedPod, err := c.Pods(ns).Get(tolerationPodName)
|
||||
framework.ExpectNoError(err)
|
||||
Expect(deployedPod.Spec.NodeName).To(Equal(nodeName))
|
||||
|
||||
By("removing the taint " + taintName + " off the node " + nodeName)
|
||||
framework.RunKubectlOrDie("taint", "nodes", nodeName, taintName+"-")
|
||||
By("verifying the node doesn't have the taint " + taintName)
|
||||
output = framework.RunKubectlOrDie("describe", "node", nodeName)
|
||||
if strings.Contains(output, taintName) {
|
||||
framework.Failf("Failed removing taint " + taintName + " of the node " + nodeName)
|
||||
}
|
||||
|
||||
By("removing the label " + labelKey + " off the node " + nodeName)
|
||||
framework.RunKubectlOrDie("label", "nodes", nodeName, labelKey+"-")
|
||||
By("verifying the node doesn't have the label " + labelKey)
|
||||
output = framework.RunKubectlOrDie("describe", "node", nodeName)
|
||||
if strings.Contains(output, labelKey) {
|
||||
framework.Failf("Failed removing label " + labelKey + " of the node " + nodeName)
|
||||
}
|
||||
})
|
||||
|
||||
// 1. Run a pod to get an available node, then delete the pod
|
||||
// 2. Taint the node with a random taint
|
||||
// 3. Try to relaunch the pod still no tolerations,
|
||||
// and the pod's nodeName specified to the name of node found in step 1
|
||||
It("validates that taints-tolerations is respected if not matching", func() {
|
||||
// launch a pod to find a node which can launch a pod. We intentionally do
|
||||
// not just take the node list and choose the first of them. Depending on the
|
||||
// cluster and the scheduler it might be that a "normal" pod cannot be
|
||||
// scheduled onto it.
|
||||
By("Trying to launch a pod without a toleration to get a node which can launch it.")
|
||||
podName := "without-toleration"
|
||||
_, err := c.Pods(ns).Create(&api.Pod{
|
||||
TypeMeta: unversioned.TypeMeta{
|
||||
Kind: "Pod",
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: podName,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: podName,
|
||||
Image: "gcr.io/google_containers/pause-amd64:3.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectNoError(framework.WaitForPodRunningInNamespace(c, podName, ns))
|
||||
pod, err := c.Pods(ns).Get(podName)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
nodeName := pod.Spec.NodeName
|
||||
err = c.Pods(ns).Delete(podName, api.NewDeleteOptions(0))
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
By("Trying to apply a random taint on the found node.")
|
||||
taintName := fmt.Sprintf("kubernetes.io/e2e-taint-key-%s", string(util.NewUUID()))
|
||||
taintValue := "testing-taint-value"
|
||||
taintEffect := string(api.TaintEffectNoSchedule)
|
||||
framework.RunKubectlOrDie("taint", "nodes", nodeName, taintName+"="+taintValue+":"+taintEffect)
|
||||
By("verifying the node has the taint " + taintName + " with the value " + taintValue)
|
||||
output := framework.RunKubectlOrDie("describe", "node", nodeName)
|
||||
requiredStrings := [][]string{
|
||||
{"Name:", nodeName},
|
||||
{"Taints:"},
|
||||
{taintName, taintValue, taintEffect},
|
||||
}
|
||||
checkOutput(output, requiredStrings)
|
||||
|
||||
By("Trying to apply a random label on the found node.")
|
||||
labelKey := fmt.Sprintf("kubernetes.io/e2e-label-key-%s", string(util.NewUUID()))
|
||||
labelValue := "testing-label-value"
|
||||
framework.RunKubectlOrDie("label", "nodes", nodeName, labelKey+"="+labelValue)
|
||||
By("verifying the node has the label " + labelKey + " with the value " + labelValue)
|
||||
labelOutput := framework.RunKubectlOrDie("describe", "node", nodeName)
|
||||
labelOutputRequiredStrings := [][]string{
|
||||
{"Name:", nodeName},
|
||||
{"Labels:"},
|
||||
{labelKey + "=" + labelValue},
|
||||
}
|
||||
checkOutput(labelOutput, labelOutputRequiredStrings)
|
||||
|
||||
By("Trying to relaunch the pod, still no tolerations.")
|
||||
podNameNoTolerations := "still-no-tolerations"
|
||||
podNoTolerations := api.Pod{
|
||||
TypeMeta: unversioned.TypeMeta{
|
||||
Kind: "Pod",
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: podNameNoTolerations,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
NodeSelector: map[string]string{labelKey: labelValue},
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: podNameNoTolerations,
|
||||
Image: "gcr.io/google_containers/pause-amd64:3.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err = c.Pods(ns).Create(&podNoTolerations)
|
||||
framework.ExpectNoError(err)
|
||||
// Wait a bit to allow scheduler to do its thing
|
||||
// TODO: this is brittle; there's no guarantee the scheduler will have run in 10 seconds.
|
||||
framework.Logf("Sleeping 10 seconds and crossing our fingers that scheduler will run in that time.")
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
verifyResult(c, podNameNoTolerations, ns)
|
||||
cleanupPods(c, ns)
|
||||
|
||||
By("removing the taint " + taintName + " off the node " + nodeName)
|
||||
framework.RunKubectlOrDie("taint", "nodes", nodeName, taintName+"-")
|
||||
By("verifying the node doesn't have the taint " + taintName)
|
||||
output = framework.RunKubectlOrDie("describe", "node", nodeName)
|
||||
if strings.Contains(output, taintName) {
|
||||
framework.Failf("Failed removing taint " + taintName + " of the node " + nodeName)
|
||||
}
|
||||
|
||||
By("Trying to relaunch the same.")
|
||||
_, err = c.Pods(ns).Create(&podNoTolerations)
|
||||
framework.ExpectNoError(err)
|
||||
defer c.Pods(ns).Delete(podNameNoTolerations, api.NewDeleteOptions(0))
|
||||
|
||||
// check that pod got scheduled. We intentionally DO NOT check that the
|
||||
// pod is running because this will create a race condition with the
|
||||
// kubelet and the scheduler: the scheduler might have scheduled a pod
|
||||
// already when the kubelet does not know about its new taint yet. The
|
||||
// kubelet will then refuse to launch the pod.
|
||||
framework.ExpectNoError(framework.WaitForPodNotPending(c, ns, podNameNoTolerations))
|
||||
deployedPod, err := c.Pods(ns).Get(podNameNoTolerations)
|
||||
framework.ExpectNoError(err)
|
||||
Expect(deployedPod.Spec.NodeName).To(Equal(nodeName))
|
||||
|
||||
By("removing the label " + labelKey + " off the node " + nodeName)
|
||||
framework.RunKubectlOrDie("label", "nodes", nodeName, labelKey+"-")
|
||||
By("verifying the node doesn't have the label " + labelKey)
|
||||
output = framework.RunKubectlOrDie("describe", "node", nodeName)
|
||||
if strings.Contains(output, labelKey) {
|
||||
framework.Failf("Failed removing label " + labelKey + " of the node " + nodeName)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue