mirror of https://github.com/k3s-io/k3s
Initial node drain implementation for #3885.
It cordons (marks unschedulable) the given node, and then deletes every pod on it, optionally using a grace period. It will not delete pods managed by neither a ReplicationController nor a DaemonSet without the use of --force. Also add cordon/uncordon, which just toggle node schedulability.pull/6/head
parent
547bf75b54
commit
c6e9ad066e
|
@ -20,6 +20,7 @@ docs/man/man1/kubectl-config-use-context.1
|
||||||
docs/man/man1/kubectl-config-view.1
|
docs/man/man1/kubectl-config-view.1
|
||||||
docs/man/man1/kubectl-config.1
|
docs/man/man1/kubectl-config.1
|
||||||
docs/man/man1/kubectl-convert.1
|
docs/man/man1/kubectl-convert.1
|
||||||
|
docs/man/man1/kubectl-cordon.1
|
||||||
docs/man/man1/kubectl-create-namespace.1
|
docs/man/man1/kubectl-create-namespace.1
|
||||||
docs/man/man1/kubectl-create-secret-docker-registry.1
|
docs/man/man1/kubectl-create-secret-docker-registry.1
|
||||||
docs/man/man1/kubectl-create-secret-generic.1
|
docs/man/man1/kubectl-create-secret-generic.1
|
||||||
|
@ -27,6 +28,7 @@ docs/man/man1/kubectl-create-secret.1
|
||||||
docs/man/man1/kubectl-create.1
|
docs/man/man1/kubectl-create.1
|
||||||
docs/man/man1/kubectl-delete.1
|
docs/man/man1/kubectl-delete.1
|
||||||
docs/man/man1/kubectl-describe.1
|
docs/man/man1/kubectl-describe.1
|
||||||
|
docs/man/man1/kubectl-drain.1
|
||||||
docs/man/man1/kubectl-edit.1
|
docs/man/man1/kubectl-edit.1
|
||||||
docs/man/man1/kubectl-exec.1
|
docs/man/man1/kubectl-exec.1
|
||||||
docs/man/man1/kubectl-explain.1
|
docs/man/man1/kubectl-explain.1
|
||||||
|
@ -43,6 +45,7 @@ docs/man/man1/kubectl-rolling-update.1
|
||||||
docs/man/man1/kubectl-run.1
|
docs/man/man1/kubectl-run.1
|
||||||
docs/man/man1/kubectl-scale.1
|
docs/man/man1/kubectl-scale.1
|
||||||
docs/man/man1/kubectl-stop.1
|
docs/man/man1/kubectl-stop.1
|
||||||
|
docs/man/man1/kubectl-uncordon.1
|
||||||
docs/man/man1/kubectl-version.1
|
docs/man/man1/kubectl-version.1
|
||||||
docs/man/man1/kubectl.1
|
docs/man/man1/kubectl.1
|
||||||
docs/user-guide/kubectl/kubectl.md
|
docs/user-guide/kubectl/kubectl.md
|
||||||
|
@ -61,6 +64,7 @@ docs/user-guide/kubectl/kubectl_config_unset.md
|
||||||
docs/user-guide/kubectl/kubectl_config_use-context.md
|
docs/user-guide/kubectl/kubectl_config_use-context.md
|
||||||
docs/user-guide/kubectl/kubectl_config_view.md
|
docs/user-guide/kubectl/kubectl_config_view.md
|
||||||
docs/user-guide/kubectl/kubectl_convert.md
|
docs/user-guide/kubectl/kubectl_convert.md
|
||||||
|
docs/user-guide/kubectl/kubectl_cordon.md
|
||||||
docs/user-guide/kubectl/kubectl_create.md
|
docs/user-guide/kubectl/kubectl_create.md
|
||||||
docs/user-guide/kubectl/kubectl_create_namespace.md
|
docs/user-guide/kubectl/kubectl_create_namespace.md
|
||||||
docs/user-guide/kubectl/kubectl_create_secret.md
|
docs/user-guide/kubectl/kubectl_create_secret.md
|
||||||
|
@ -68,6 +72,7 @@ docs/user-guide/kubectl/kubectl_create_secret_docker-registry.md
|
||||||
docs/user-guide/kubectl/kubectl_create_secret_generic.md
|
docs/user-guide/kubectl/kubectl_create_secret_generic.md
|
||||||
docs/user-guide/kubectl/kubectl_delete.md
|
docs/user-guide/kubectl/kubectl_delete.md
|
||||||
docs/user-guide/kubectl/kubectl_describe.md
|
docs/user-guide/kubectl/kubectl_describe.md
|
||||||
|
docs/user-guide/kubectl/kubectl_drain.md
|
||||||
docs/user-guide/kubectl/kubectl_edit.md
|
docs/user-guide/kubectl/kubectl_edit.md
|
||||||
docs/user-guide/kubectl/kubectl_exec.md
|
docs/user-guide/kubectl/kubectl_exec.md
|
||||||
docs/user-guide/kubectl/kubectl_explain.md
|
docs/user-guide/kubectl/kubectl_explain.md
|
||||||
|
@ -83,4 +88,5 @@ docs/user-guide/kubectl/kubectl_replace.md
|
||||||
docs/user-guide/kubectl/kubectl_rolling-update.md
|
docs/user-guide/kubectl/kubectl_rolling-update.md
|
||||||
docs/user-guide/kubectl/kubectl_run.md
|
docs/user-guide/kubectl/kubectl_run.md
|
||||||
docs/user-guide/kubectl/kubectl_scale.md
|
docs/user-guide/kubectl/kubectl_scale.md
|
||||||
|
docs/user-guide/kubectl/kubectl_uncordon.md
|
||||||
docs/user-guide/kubectl/kubectl_version.md
|
docs/user-guide/kubectl/kubectl_version.md
|
||||||
|
|
|
@ -448,6 +448,7 @@ function kube::build::source_targets() {
|
||||||
test
|
test
|
||||||
third_party
|
third_party
|
||||||
contrib/completions/bash/kubectl
|
contrib/completions/bash/kubectl
|
||||||
|
contrib/mesos
|
||||||
.generated_docs
|
.generated_docs
|
||||||
)
|
)
|
||||||
if [ -n "${KUBERNETES_CONTRIB:-}" ]; then
|
if [ -n "${KUBERNETES_CONTRIB:-}" ]; then
|
||||||
|
|
|
@ -1140,6 +1140,125 @@ _kubectl_scale()
|
||||||
must_have_one_noun=()
|
must_have_one_noun=()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_kubectl_cordon()
|
||||||
|
{
|
||||||
|
last_command="kubectl_cordon"
|
||||||
|
commands=()
|
||||||
|
|
||||||
|
flags=()
|
||||||
|
two_word_flags=()
|
||||||
|
flags_with_completion=()
|
||||||
|
flags_completion=()
|
||||||
|
|
||||||
|
flags+=("--alsologtostderr")
|
||||||
|
flags+=("--api-version=")
|
||||||
|
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+=("--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=()
|
||||||
|
}
|
||||||
|
|
||||||
|
_kubectl_drain()
|
||||||
|
{
|
||||||
|
last_command="kubectl_drain"
|
||||||
|
commands=()
|
||||||
|
|
||||||
|
flags=()
|
||||||
|
two_word_flags=()
|
||||||
|
flags_with_completion=()
|
||||||
|
flags_completion=()
|
||||||
|
|
||||||
|
flags+=("--force")
|
||||||
|
flags+=("--grace-period=")
|
||||||
|
flags+=("--alsologtostderr")
|
||||||
|
flags+=("--api-version=")
|
||||||
|
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+=("--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=()
|
||||||
|
}
|
||||||
|
|
||||||
|
_kubectl_uncordon()
|
||||||
|
{
|
||||||
|
last_command="kubectl_uncordon"
|
||||||
|
commands=()
|
||||||
|
|
||||||
|
flags=()
|
||||||
|
two_word_flags=()
|
||||||
|
flags_with_completion=()
|
||||||
|
flags_completion=()
|
||||||
|
|
||||||
|
flags+=("--alsologtostderr")
|
||||||
|
flags+=("--api-version=")
|
||||||
|
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+=("--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=()
|
||||||
|
}
|
||||||
|
|
||||||
_kubectl_attach()
|
_kubectl_attach()
|
||||||
{
|
{
|
||||||
last_command="kubectl_attach"
|
last_command="kubectl_attach"
|
||||||
|
@ -2241,6 +2360,9 @@ _kubectl()
|
||||||
commands+=("logs")
|
commands+=("logs")
|
||||||
commands+=("rolling-update")
|
commands+=("rolling-update")
|
||||||
commands+=("scale")
|
commands+=("scale")
|
||||||
|
commands+=("cordon")
|
||||||
|
commands+=("drain")
|
||||||
|
commands+=("uncordon")
|
||||||
commands+=("attach")
|
commands+=("attach")
|
||||||
commands+=("exec")
|
commands+=("exec")
|
||||||
commands+=("port-forward")
|
commands+=("port-forward")
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
.TH "KUBERNETES" "1" " kubernetes User Manuals" "Eric Paris" "Jan 2015" ""
|
||||||
|
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
.PP
|
||||||
|
kubectl cordon \- Mark node as unschedulable
|
||||||
|
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.PP
|
||||||
|
\fBkubectl cordon\fP [OPTIONS]
|
||||||
|
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.PP
|
||||||
|
Mark node as unschedulable.
|
||||||
|
|
||||||
|
|
||||||
|
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||||
|
.PP
|
||||||
|
\fB\-\-alsologtostderr\fP=false
|
||||||
|
log to standard error as well as files
|
||||||
|
|
||||||
|
.PP
|
||||||
|
\fB\-\-api\-version\fP=""
|
||||||
|
The API version to use when talking to the server
|
||||||
|
|
||||||
|
.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
|
||||||
|
# Mark node "foo" as unschedulable.
|
||||||
|
$ kubectl cordon foo
|
||||||
|
|
||||||
|
|
||||||
|
.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!
|
|
@ -0,0 +1,157 @@
|
||||||
|
.TH "KUBERNETES" "1" " kubernetes User Manuals" "Eric Paris" "Jan 2015" ""
|
||||||
|
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
.PP
|
||||||
|
kubectl drain \- Drain node in preparation for maintenance
|
||||||
|
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.PP
|
||||||
|
\fBkubectl drain\fP [OPTIONS]
|
||||||
|
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.PP
|
||||||
|
Drain node in preparation for maintenance.
|
||||||
|
|
||||||
|
.PP
|
||||||
|
The given node will be marked unschedulable to prevent new pods from arriving.
|
||||||
|
Then drain deletes all pods except mirror pods (which cannot be deleted through
|
||||||
|
the API server). If there are any pods that are neither mirror pods nor
|
||||||
|
managed by a ReplicationController or DaemonSet, then drain will not delete any
|
||||||
|
pods unless you use \-\-force.
|
||||||
|
|
||||||
|
.PP
|
||||||
|
When you are ready to put the node back into service, use kubectl uncordon, which
|
||||||
|
will make the node schedulable again.
|
||||||
|
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.PP
|
||||||
|
\fB\-\-force\fP=false
|
||||||
|
Continue even if there are pods not managed by a ReplicationController or DaemonSet.
|
||||||
|
|
||||||
|
.PP
|
||||||
|
\fB\-\-grace\-period\fP=\-1
|
||||||
|
Period of time in seconds given to each pod to terminate gracefully. If negative, the default value specified in the pod will be used.
|
||||||
|
|
||||||
|
|
||||||
|
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||||
|
.PP
|
||||||
|
\fB\-\-alsologtostderr\fP=false
|
||||||
|
log to standard error as well as files
|
||||||
|
|
||||||
|
.PP
|
||||||
|
\fB\-\-api\-version\fP=""
|
||||||
|
The API version to use when talking to the server
|
||||||
|
|
||||||
|
.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
|
||||||
|
# Drain node "foo", even if there are pods not managed by a ReplicationController or DaemonSet on it.
|
||||||
|
$ kubectl drain foo \-\-force
|
||||||
|
|
||||||
|
# As above, but abort if there are pods not managed by a ReplicationController or DaemonSet, and use a grace period of 15 minutes.
|
||||||
|
$ kubectl drain foo \-\-grace\-period=900
|
||||||
|
|
||||||
|
|
||||||
|
.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!
|
|
@ -0,0 +1,133 @@
|
||||||
|
.TH "KUBERNETES" "1" " kubernetes User Manuals" "Eric Paris" "Jan 2015" ""
|
||||||
|
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
.PP
|
||||||
|
kubectl uncordon \- Mark node as schedulable
|
||||||
|
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.PP
|
||||||
|
\fBkubectl uncordon\fP [OPTIONS]
|
||||||
|
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.PP
|
||||||
|
Mark node as schedulable.
|
||||||
|
|
||||||
|
|
||||||
|
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||||
|
.PP
|
||||||
|
\fB\-\-alsologtostderr\fP=false
|
||||||
|
log to standard error as well as files
|
||||||
|
|
||||||
|
.PP
|
||||||
|
\fB\-\-api\-version\fP=""
|
||||||
|
The API version to use when talking to the server
|
||||||
|
|
||||||
|
.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
|
||||||
|
# Mark node "foo" as schedulable.
|
||||||
|
$ kubectl uncordon foo
|
||||||
|
|
||||||
|
|
||||||
|
.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!
|
|
@ -116,7 +116,7 @@ Find more information at
|
||||||
|
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
.PP
|
.PP
|
||||||
\fBkubectl\-get(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\-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\-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\-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\-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,
|
||||||
|
|
||||||
|
|
||||||
.SH HISTORY
|
.SH HISTORY
|
||||||
|
|
|
@ -85,9 +85,11 @@ kubectl
|
||||||
* [kubectl cluster-info](kubectl_cluster-info.md) - Display cluster info
|
* [kubectl cluster-info](kubectl_cluster-info.md) - Display cluster info
|
||||||
* [kubectl config](kubectl_config.md) - config modifies kubeconfig files
|
* [kubectl config](kubectl_config.md) - config modifies kubeconfig files
|
||||||
* [kubectl convert](kubectl_convert.md) - Convert config files between different API versions
|
* [kubectl convert](kubectl_convert.md) - Convert config files between different API versions
|
||||||
|
* [kubectl cordon](kubectl_cordon.md) - Mark node as unschedulable
|
||||||
* [kubectl create](kubectl_create.md) - Create a resource by filename or stdin
|
* [kubectl create](kubectl_create.md) - Create a resource by filename or stdin
|
||||||
* [kubectl delete](kubectl_delete.md) - Delete resources by filenames, stdin, resources and names, or by resources and label selector.
|
* [kubectl delete](kubectl_delete.md) - Delete resources by filenames, stdin, resources and names, or by resources and label selector.
|
||||||
* [kubectl describe](kubectl_describe.md) - Show details of a specific resource or group of resources
|
* [kubectl describe](kubectl_describe.md) - Show details of a specific resource or group of resources
|
||||||
|
* [kubectl drain](kubectl_drain.md) - Drain node in preparation for maintenance
|
||||||
* [kubectl edit](kubectl_edit.md) - Edit a resource on the server
|
* [kubectl edit](kubectl_edit.md) - Edit a resource on the server
|
||||||
* [kubectl exec](kubectl_exec.md) - Execute a command in a container.
|
* [kubectl exec](kubectl_exec.md) - Execute a command in a container.
|
||||||
* [kubectl explain](kubectl_explain.md) - Documentation of resources.
|
* [kubectl explain](kubectl_explain.md) - Documentation of resources.
|
||||||
|
@ -103,9 +105,10 @@ kubectl
|
||||||
* [kubectl rolling-update](kubectl_rolling-update.md) - Perform a rolling update of the given ReplicationController.
|
* [kubectl rolling-update](kubectl_rolling-update.md) - Perform a rolling update of the given ReplicationController.
|
||||||
* [kubectl run](kubectl_run.md) - Run a particular image on the cluster.
|
* [kubectl run](kubectl_run.md) - Run a particular image on the cluster.
|
||||||
* [kubectl scale](kubectl_scale.md) - Set a new size for a Replication Controller, Job, or Deployment.
|
* [kubectl scale](kubectl_scale.md) - Set a new size for a Replication Controller, Job, or Deployment.
|
||||||
|
* [kubectl uncordon](kubectl_uncordon.md) - Mark node as schedulable
|
||||||
* [kubectl version](kubectl_version.md) - Print the client and server version information.
|
* [kubectl version](kubectl_version.md) - Print the client and server version information.
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 8-Dec-2015
|
###### Auto generated by spf13/cobra on 4-Jan-2016
|
||||||
|
|
||||||
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
||||||
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl.md?pixel)]()
|
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl.md?pixel)]()
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
<!-- 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 cordon
|
||||||
|
|
||||||
|
Mark node as unschedulable
|
||||||
|
|
||||||
|
### Synopsis
|
||||||
|
|
||||||
|
|
||||||
|
Mark node as unschedulable.
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
kubectl cordon NODE
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
# Mark node "foo" as unschedulable.
|
||||||
|
$ kubectl cordon foo
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options inherited from parent commands
|
||||||
|
|
||||||
|
```
|
||||||
|
--alsologtostderr[=false]: log to standard error as well as files
|
||||||
|
--api-version="": The API version to use when talking to the server
|
||||||
|
--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 4-Jan-2016
|
||||||
|
|
||||||
|
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
||||||
|
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_cordon.md?pixel)]()
|
||||||
|
<!-- END MUNGE: GENERATED_ANALYTICS -->
|
|
@ -0,0 +1,107 @@
|
||||||
|
<!-- 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 drain
|
||||||
|
|
||||||
|
Drain node in preparation for maintenance
|
||||||
|
|
||||||
|
### Synopsis
|
||||||
|
|
||||||
|
|
||||||
|
Drain node in preparation for maintenance.
|
||||||
|
|
||||||
|
The given node will be marked unschedulable to prevent new pods from arriving.
|
||||||
|
Then drain deletes all pods except mirror pods (which cannot be deleted through
|
||||||
|
the API server). If there are any pods that are neither mirror pods nor
|
||||||
|
managed by a ReplicationController or DaemonSet, then drain will not delete any
|
||||||
|
pods unless you use --force.
|
||||||
|
|
||||||
|
When you are ready to put the node back into service, use kubectl uncordon, which
|
||||||
|
will make the node schedulable again.
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
kubectl drain NODE
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
# Drain node "foo", even if there are pods not managed by a ReplicationController or DaemonSet on it.
|
||||||
|
$ kubectl drain foo --force
|
||||||
|
|
||||||
|
# As above, but abort if there are pods not managed by a ReplicationController or DaemonSet, and use a grace period of 15 minutes.
|
||||||
|
$ kubectl drain foo --grace-period=900
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
--force[=false]: Continue even if there are pods not managed by a ReplicationController or DaemonSet.
|
||||||
|
--grace-period=-1: Period of time in seconds given to each pod to terminate gracefully. If negative, the default value specified in the pod will be used.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options inherited from parent commands
|
||||||
|
|
||||||
|
```
|
||||||
|
--alsologtostderr[=false]: log to standard error as well as files
|
||||||
|
--api-version="": The API version to use when talking to the server
|
||||||
|
--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 6-Jan-2016
|
||||||
|
|
||||||
|
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
||||||
|
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_drain.md?pixel)]()
|
||||||
|
<!-- END MUNGE: GENERATED_ANALYTICS -->
|
|
@ -0,0 +1,88 @@
|
||||||
|
<!-- 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 uncordon
|
||||||
|
|
||||||
|
Mark node as schedulable
|
||||||
|
|
||||||
|
### Synopsis
|
||||||
|
|
||||||
|
|
||||||
|
Mark node as schedulable.
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
kubectl uncordon NODE
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
# Mark node "foo" as schedulable.
|
||||||
|
$ kubectl uncordon foo
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options inherited from parent commands
|
||||||
|
|
||||||
|
```
|
||||||
|
--alsologtostderr[=false]: log to standard error as well as files
|
||||||
|
--api-version="": The API version to use when talking to the server
|
||||||
|
--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 4-Jan-2016
|
||||||
|
|
||||||
|
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
||||||
|
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_uncordon.md?pixel)]()
|
||||||
|
<!-- END MUNGE: GENERATED_ANALYTICS -->
|
|
@ -162,6 +162,9 @@ Find more information at https://github.com/kubernetes/kubernetes.`,
|
||||||
cmds.AddCommand(NewCmdLogs(f, out))
|
cmds.AddCommand(NewCmdLogs(f, out))
|
||||||
cmds.AddCommand(NewCmdRollingUpdate(f, out))
|
cmds.AddCommand(NewCmdRollingUpdate(f, out))
|
||||||
cmds.AddCommand(NewCmdScale(f, out))
|
cmds.AddCommand(NewCmdScale(f, out))
|
||||||
|
cmds.AddCommand(NewCmdCordon(f, out))
|
||||||
|
cmds.AddCommand(NewCmdDrain(f, out))
|
||||||
|
cmds.AddCommand(NewCmdUncordon(f, out))
|
||||||
|
|
||||||
cmds.AddCommand(NewCmdAttach(f, in, out, err))
|
cmds.AddCommand(NewCmdAttach(f, in, out, err))
|
||||||
cmds.AddCommand(NewCmdExec(f, in, out, err))
|
cmds.AddCommand(NewCmdExec(f, in, out, err))
|
||||||
|
|
|
@ -232,6 +232,7 @@ func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) {
|
||||||
fakeClient := t.Client.(*fake.RESTClient)
|
fakeClient := t.Client.(*fake.RESTClient)
|
||||||
c := client.NewOrDie(t.ClientConfig)
|
c := client.NewOrDie(t.ClientConfig)
|
||||||
c.Client = fakeClient.Client
|
c.Client = fakeClient.Client
|
||||||
|
c.ExtensionsClient.Client = fakeClient.Client
|
||||||
return c, t.Err
|
return c, t.Err
|
||||||
},
|
},
|
||||||
RESTClient: func(*meta.RESTMapping) (resource.RESTClient, error) {
|
RESTClient: func(*meta.RESTMapping) (resource.RESTClient, error) {
|
||||||
|
|
|
@ -0,0 +1,314 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015 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"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/api/meta"
|
||||||
|
"k8s.io/kubernetes/pkg/controller"
|
||||||
|
// "k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||||
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
|
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||||
|
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/types"
|
||||||
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DrainOptions struct {
|
||||||
|
client *client.Client
|
||||||
|
factory *cmdutil.Factory
|
||||||
|
Force bool
|
||||||
|
GracePeriodSeconds int
|
||||||
|
mapper meta.RESTMapper
|
||||||
|
nodeInfo *resource.Info
|
||||||
|
out io.Writer
|
||||||
|
typer runtime.ObjectTyper
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
cordon_long = `Mark node as unschedulable.
|
||||||
|
`
|
||||||
|
cordon_example = `# Mark node "foo" as unschedulable.
|
||||||
|
$ kubectl cordon foo
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewCmdCordon(f *cmdutil.Factory, out io.Writer) *cobra.Command {
|
||||||
|
options := &DrainOptions{factory: f, out: out}
|
||||||
|
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "cordon NODE",
|
||||||
|
Short: "Mark node as unschedulable",
|
||||||
|
Long: cordon_long,
|
||||||
|
Example: cordon_example,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
cmdutil.CheckErr(options.SetupDrain(cmd, args))
|
||||||
|
cmdutil.CheckErr(options.RunCordonOrUncordon(true))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
uncordon_long = `Mark node as schedulable.
|
||||||
|
`
|
||||||
|
uncordon_example = `# Mark node "foo" as schedulable.
|
||||||
|
$ kubectl uncordon foo
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewCmdUncordon(f *cmdutil.Factory, out io.Writer) *cobra.Command {
|
||||||
|
options := &DrainOptions{factory: f, out: out}
|
||||||
|
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "uncordon NODE",
|
||||||
|
Short: "Mark node as schedulable",
|
||||||
|
Long: uncordon_long,
|
||||||
|
Example: uncordon_example,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
cmdutil.CheckErr(options.SetupDrain(cmd, args))
|
||||||
|
cmdutil.CheckErr(options.RunCordonOrUncordon(false))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
drain_long = `Drain node in preparation for maintenance.
|
||||||
|
|
||||||
|
The given node will be marked unschedulable to prevent new pods from arriving.
|
||||||
|
Then drain deletes all pods except mirror pods (which cannot be deleted through
|
||||||
|
the API server). If there are any pods that are neither mirror pods nor
|
||||||
|
managed by a ReplicationController or DaemonSet, then drain will not delete any
|
||||||
|
pods unless you use --force.
|
||||||
|
|
||||||
|
When you are ready to put the node back into service, use kubectl uncordon, which
|
||||||
|
will make the node schedulable again.
|
||||||
|
`
|
||||||
|
drain_example = `# Drain node "foo", even if there are pods not managed by a ReplicationController or DaemonSet on it.
|
||||||
|
$ kubectl drain foo --force
|
||||||
|
|
||||||
|
# As above, but abort if there are pods not managed by a ReplicationController or DaemonSet, and use a grace period of 15 minutes.
|
||||||
|
$ kubectl drain foo --grace-period=900
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewCmdDrain(f *cmdutil.Factory, out io.Writer) *cobra.Command {
|
||||||
|
options := &DrainOptions{factory: f, out: out}
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "drain NODE",
|
||||||
|
Short: "Drain node in preparation for maintenance",
|
||||||
|
Long: drain_long,
|
||||||
|
Example: drain_example,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
cmdutil.CheckErr(options.SetupDrain(cmd, args))
|
||||||
|
cmdutil.CheckErr(options.RunDrain())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.Flags().BoolVar(&options.Force, "force", false, "Continue even if there are pods not managed by a ReplicationController or DaemonSet.")
|
||||||
|
cmd.Flags().IntVar(&options.GracePeriodSeconds, "grace-period", -1, "Period of time in seconds given to each pod to terminate gracefully. If negative, the default value specified in the pod will be used.")
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupDrain populates some fields from the factory, grabs command line
|
||||||
|
// arguments and looks up the node using Builder
|
||||||
|
func (o *DrainOptions) SetupDrain(cmd *cobra.Command, args []string) error {
|
||||||
|
var err error
|
||||||
|
if len(args) != 1 {
|
||||||
|
return cmdutil.UsageError(cmd, fmt.Sprintf("USAGE: %s [flags]", cmd.Use))
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.client, err = o.factory.Client(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
o.mapper, o.typer = o.factory.Object()
|
||||||
|
|
||||||
|
cmdNamespace, _, err := o.factory.DefaultNamespace()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r := o.factory.NewBuilder().
|
||||||
|
NamespaceParam(cmdNamespace).DefaultNamespace().
|
||||||
|
ResourceNames("node", args[0]).
|
||||||
|
Do()
|
||||||
|
|
||||||
|
if err = r.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Visit(func(info *resource.Info, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
o.nodeInfo = info
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunDrain runs the 'drain' command
|
||||||
|
func (o *DrainOptions) RunDrain() error {
|
||||||
|
if err := o.RunCordonOrUncordon(true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pods, err := o.getPodsForDeletion()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = o.deletePods(pods); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmdutil.PrintSuccess(o.mapper, false, o.out, "node", o.nodeInfo.Name, "drained")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPodsForDeletion returns all the pods we're going to delete. If there are
|
||||||
|
// any unmanaged pods and the user didn't pass --force, we return that list in
|
||||||
|
// an error.
|
||||||
|
func (o *DrainOptions) getPodsForDeletion() ([]api.Pod, error) {
|
||||||
|
pods := []api.Pod{}
|
||||||
|
podList, err := o.client.Pods(api.NamespaceAll).List(api.ListOptions{FieldSelector: fields.SelectorFromSet(fields.Set{"spec.nodeName": o.nodeInfo.Name})})
|
||||||
|
if err != nil {
|
||||||
|
return pods, err
|
||||||
|
}
|
||||||
|
unreplicatedPodNames := []string{}
|
||||||
|
|
||||||
|
for _, pod := range podList.Items {
|
||||||
|
_, found := pod.ObjectMeta.Annotations[types.ConfigMirrorAnnotationKey]
|
||||||
|
if found {
|
||||||
|
// Skip mirror pod
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
replicated := false
|
||||||
|
|
||||||
|
creatorRef, found := pod.ObjectMeta.Annotations[controller.CreatedByAnnotation]
|
||||||
|
if found {
|
||||||
|
// Now verify that the specified creator actually exists.
|
||||||
|
var sr api.SerializedReference
|
||||||
|
err := api.Scheme.DecodeInto([]byte(creatorRef), &sr)
|
||||||
|
if err != nil {
|
||||||
|
return pods, err
|
||||||
|
}
|
||||||
|
if sr.Reference.Kind == "ReplicationController" {
|
||||||
|
rc, err := o.client.ReplicationControllers(sr.Reference.Namespace).Get(sr.Reference.Name)
|
||||||
|
// Assume the only reason for an error is because the RC is
|
||||||
|
// gone/missing, not for any other cause. TODO(mml): something more
|
||||||
|
// sophisticated than this
|
||||||
|
if err == nil && rc != nil {
|
||||||
|
replicated = true
|
||||||
|
}
|
||||||
|
} else if sr.Reference.Kind == "DaemonSet" {
|
||||||
|
ds, err := o.client.DaemonSets(sr.Reference.Namespace).Get(sr.Reference.Name)
|
||||||
|
|
||||||
|
// Assume the only reason for an error is because the DaemonSet is
|
||||||
|
// gone/missing, not for any other cause. TODO(mml): something more
|
||||||
|
// sophisticated than this
|
||||||
|
if err == nil && ds != nil {
|
||||||
|
replicated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if replicated || o.Force {
|
||||||
|
pods = append(pods, pod)
|
||||||
|
}
|
||||||
|
if !replicated {
|
||||||
|
unreplicatedPodNames = append(unreplicatedPodNames, pod.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(unreplicatedPodNames) > 0 {
|
||||||
|
joined := strings.Join(unreplicatedPodNames, ", ")
|
||||||
|
if !o.Force {
|
||||||
|
return pods, fmt.Errorf("refusing to continue due to pods managed by neither a ReplicationController nor a DaemonSet: %s (use --force to override)", joined)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(o.out, "WARNING: About to delete these pods managed by neither a ReplicationController nor a DaemonSet: %s\n", joined)
|
||||||
|
}
|
||||||
|
return pods, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// deletePods deletes the pods on the api server
|
||||||
|
func (o *DrainOptions) deletePods(pods []api.Pod) error {
|
||||||
|
deleteOptions := api.DeleteOptions{}
|
||||||
|
if o.GracePeriodSeconds >= 0 {
|
||||||
|
gracePeriodSeconds := int64(o.GracePeriodSeconds)
|
||||||
|
deleteOptions.GracePeriodSeconds = &gracePeriodSeconds
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pod := range pods {
|
||||||
|
err := o.client.Pods(pod.Namespace).Delete(pod.Name, &deleteOptions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmdutil.PrintSuccess(o.mapper, false, o.out, "pod", pod.Name, "deleted")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunCordonOrUncordon runs either Cordon or Uncordon. The desired value for
|
||||||
|
// "Unschedulable" is passed as the first arg.
|
||||||
|
func (o *DrainOptions) RunCordonOrUncordon(desired bool) error {
|
||||||
|
cmdNamespace, _, err := o.factory.DefaultNamespace()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.nodeInfo.Mapping.GroupVersionKind.Kind == "Node" {
|
||||||
|
unsched := reflect.ValueOf(o.nodeInfo.Object).Elem().FieldByName("Spec").FieldByName("Unschedulable")
|
||||||
|
if unsched.Bool() == desired {
|
||||||
|
cmdutil.PrintSuccess(o.mapper, false, o.out, o.nodeInfo.Mapping.Resource, o.nodeInfo.Name, already(desired))
|
||||||
|
} else {
|
||||||
|
helper := resource.NewHelper(o.client, o.nodeInfo.Mapping)
|
||||||
|
unsched.SetBool(desired)
|
||||||
|
_, err := helper.Replace(cmdNamespace, o.nodeInfo.Name, true, o.nodeInfo.Object)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmdutil.PrintSuccess(o.mapper, false, o.out, o.nodeInfo.Mapping.Resource, o.nodeInfo.Name, changed(desired))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cmdutil.PrintSuccess(o.mapper, false, o.out, o.nodeInfo.Mapping.Resource, o.nodeInfo.Name, "skipped")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// already() and changed() return suitable strings for {un,}cordoning
|
||||||
|
|
||||||
|
func already(desired bool) string {
|
||||||
|
if desired {
|
||||||
|
return "already cordoned"
|
||||||
|
}
|
||||||
|
return "already uncordoned"
|
||||||
|
}
|
||||||
|
|
||||||
|
func changed(desired bool) string {
|
||||||
|
if desired {
|
||||||
|
return "cordoned"
|
||||||
|
}
|
||||||
|
return "uncordoned"
|
||||||
|
}
|
|
@ -0,0 +1,450 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015 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"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||||
|
"k8s.io/kubernetes/pkg/client/unversioned/fake"
|
||||||
|
"k8s.io/kubernetes/pkg/controller"
|
||||||
|
"k8s.io/kubernetes/pkg/conversion"
|
||||||
|
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||||
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var node *api.Node
|
||||||
|
var cordoned_node *api.Node
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
// Create a node.
|
||||||
|
node = &api.Node{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "node",
|
||||||
|
CreationTimestamp: unversioned.Time{time.Now()},
|
||||||
|
},
|
||||||
|
Spec: api.NodeSpec{
|
||||||
|
ExternalID: "node",
|
||||||
|
},
|
||||||
|
Status: api.NodeStatus{},
|
||||||
|
}
|
||||||
|
clone, _ := conversion.NewCloner().DeepCopy(node)
|
||||||
|
|
||||||
|
// A copy of the same node, but cordoned.
|
||||||
|
cordoned_node = clone.(*api.Node)
|
||||||
|
cordoned_node.Spec.Unschedulable = true
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCordon(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
description string
|
||||||
|
node *api.Node
|
||||||
|
expected *api.Node
|
||||||
|
cmd func(*cmdutil.Factory, io.Writer) *cobra.Command
|
||||||
|
arg string
|
||||||
|
expectFatal bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
description: "node/node syntax",
|
||||||
|
node: cordoned_node,
|
||||||
|
expected: node,
|
||||||
|
cmd: NewCmdUncordon,
|
||||||
|
arg: "node/node",
|
||||||
|
expectFatal: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "uncordon for real",
|
||||||
|
node: cordoned_node,
|
||||||
|
expected: node,
|
||||||
|
cmd: NewCmdUncordon,
|
||||||
|
arg: "node",
|
||||||
|
expectFatal: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "uncordon does nothing",
|
||||||
|
node: node,
|
||||||
|
expected: node,
|
||||||
|
cmd: NewCmdUncordon,
|
||||||
|
arg: "node",
|
||||||
|
expectFatal: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "cordon does nothing",
|
||||||
|
node: cordoned_node,
|
||||||
|
expected: cordoned_node,
|
||||||
|
cmd: NewCmdCordon,
|
||||||
|
arg: "node",
|
||||||
|
expectFatal: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "cordon for real",
|
||||||
|
node: node,
|
||||||
|
expected: cordoned_node,
|
||||||
|
cmd: NewCmdCordon,
|
||||||
|
arg: "node",
|
||||||
|
expectFatal: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "cordon missing node",
|
||||||
|
node: node,
|
||||||
|
expected: node,
|
||||||
|
cmd: NewCmdCordon,
|
||||||
|
arg: "bar",
|
||||||
|
expectFatal: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "uncordon missing node",
|
||||||
|
node: node,
|
||||||
|
expected: node,
|
||||||
|
cmd: NewCmdUncordon,
|
||||||
|
arg: "bar",
|
||||||
|
expectFatal: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
f, tf, codec := NewAPIFactory()
|
||||||
|
new_node := &api.Node{}
|
||||||
|
updated := false
|
||||||
|
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"):
|
||||||
|
return &http.Response{StatusCode: 200, Body: objBody(codec, test.node)}, nil
|
||||||
|
case m.isFor("GET", "/nodes/bar"):
|
||||||
|
return &http.Response{StatusCode: 404, Body: stringBody("nope")}, nil
|
||||||
|
case m.isFor("PUT", "/nodes/node"):
|
||||||
|
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 !reflect.DeepEqual(test.expected.Spec, new_node.Spec) {
|
||||||
|
t.Fatalf("%s: expected:\n%v\nsaw:\n%v\n", test.description, test.expected.Spec, new_node.Spec)
|
||||||
|
}
|
||||||
|
updated = true
|
||||||
|
return &http.Response{StatusCode: 200, 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 = &client.Config{GroupVersion: testapi.Default.GroupVersion()}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
cmd := test.cmd(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([]string{test.arg})
|
||||||
|
cmd.Execute()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if test.expectFatal {
|
||||||
|
if !saw_fatal {
|
||||||
|
t.Fatalf("%s: unexpected non-error", test.description)
|
||||||
|
}
|
||||||
|
if updated {
|
||||||
|
t.Fatalf("%s: unexpcted update", test.description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !test.expectFatal && saw_fatal {
|
||||||
|
t.Fatalf("%s: unexpected error", test.description)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(test.expected.Spec, test.node.Spec) && !updated {
|
||||||
|
t.Fatalf("%s: node never updated", test.description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDrain(t *testing.T) {
|
||||||
|
labels := make(map[string]string)
|
||||||
|
labels["my_key"] = "my_value"
|
||||||
|
|
||||||
|
rc := api.ReplicationController{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "rc",
|
||||||
|
Namespace: "default",
|
||||||
|
CreationTimestamp: unversioned.Time{time.Now()},
|
||||||
|
Labels: labels,
|
||||||
|
SelfLink: testapi.Default.SelfLink("replicationcontrollers", "rc"),
|
||||||
|
},
|
||||||
|
Spec: api.ReplicationControllerSpec{
|
||||||
|
Selector: labels,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_anno := make(map[string]string)
|
||||||
|
rc_anno[controller.CreatedByAnnotation] = refJson(t, &rc)
|
||||||
|
|
||||||
|
replicated_pod := api.Pod{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "bar",
|
||||||
|
Namespace: "default",
|
||||||
|
CreationTimestamp: unversioned.Time{time.Now()},
|
||||||
|
Labels: labels,
|
||||||
|
Annotations: rc_anno,
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
NodeName: "node",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ds := extensions.DaemonSet{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "ds",
|
||||||
|
Namespace: "default",
|
||||||
|
CreationTimestamp: unversioned.Time{time.Now()},
|
||||||
|
SelfLink: "/apis/extensions/v1beta1/namespaces/default/daemonsets/ds",
|
||||||
|
},
|
||||||
|
Spec: extensions.DaemonSetSpec{
|
||||||
|
Selector: &extensions.LabelSelector{MatchLabels: labels},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ds_anno := make(map[string]string)
|
||||||
|
ds_anno[controller.CreatedByAnnotation] = refJson(t, &ds)
|
||||||
|
|
||||||
|
ds_pod := api.Pod{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "bar",
|
||||||
|
Namespace: "default",
|
||||||
|
CreationTimestamp: unversioned.Time{time.Now()},
|
||||||
|
Labels: labels,
|
||||||
|
Annotations: ds_anno,
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
NodeName: "node",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
naked_pod := api.Pod{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "bar",
|
||||||
|
Namespace: "default",
|
||||||
|
CreationTimestamp: unversioned.Time{time.Now()},
|
||||||
|
Labels: labels,
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
NodeName: "node",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
description string
|
||||||
|
node *api.Node
|
||||||
|
expected *api.Node
|
||||||
|
pods []api.Pod
|
||||||
|
rcs []api.ReplicationController
|
||||||
|
args []string
|
||||||
|
expectFatal bool
|
||||||
|
expectDelete bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
description: "RC-managed pod",
|
||||||
|
node: node,
|
||||||
|
expected: cordoned_node,
|
||||||
|
pods: []api.Pod{replicated_pod},
|
||||||
|
rcs: []api.ReplicationController{rc},
|
||||||
|
args: []string{"node"},
|
||||||
|
expectFatal: false,
|
||||||
|
expectDelete: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "DS-managed pod",
|
||||||
|
node: node,
|
||||||
|
expected: cordoned_node,
|
||||||
|
pods: []api.Pod{ds_pod},
|
||||||
|
rcs: []api.ReplicationController{rc},
|
||||||
|
args: []string{"node"},
|
||||||
|
expectFatal: false,
|
||||||
|
expectDelete: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "naked pod",
|
||||||
|
node: node,
|
||||||
|
expected: cordoned_node,
|
||||||
|
pods: []api.Pod{naked_pod},
|
||||||
|
rcs: []api.ReplicationController{},
|
||||||
|
args: []string{"node"},
|
||||||
|
expectFatal: true,
|
||||||
|
expectDelete: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "naked pod with --force",
|
||||||
|
node: node,
|
||||||
|
expected: cordoned_node,
|
||||||
|
pods: []api.Pod{naked_pod},
|
||||||
|
rcs: []api.ReplicationController{},
|
||||||
|
args: []string{"node", "--force"},
|
||||||
|
expectFatal: false,
|
||||||
|
expectDelete: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "empty node",
|
||||||
|
node: node,
|
||||||
|
expected: cordoned_node,
|
||||||
|
pods: []api.Pod{},
|
||||||
|
rcs: []api.ReplicationController{rc},
|
||||||
|
args: []string{"node"},
|
||||||
|
expectFatal: false,
|
||||||
|
expectDelete: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
new_node := &api.Node{}
|
||||||
|
deleted := 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"):
|
||||||
|
return &http.Response{StatusCode: 200, Body: objBody(codec, test.node)}, nil
|
||||||
|
case m.isFor("GET", "/namespaces/default/replicationcontrollers/rc"):
|
||||||
|
return &http.Response{StatusCode: 200, Body: objBody(codec, &test.rcs[0])}, nil
|
||||||
|
case m.isFor("GET", "/namespaces/default/daemonsets/ds"):
|
||||||
|
return &http.Response{StatusCode: 200, Body: objBody(testapi.Extensions.Codec(), &ds)}, nil
|
||||||
|
case m.isFor("GET", "/pods"):
|
||||||
|
values, err := url.ParseQuery(req.URL.RawQuery)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s: unexpected error: %v", test.description, err)
|
||||||
|
}
|
||||||
|
get_params := make(url.Values)
|
||||||
|
get_params["fieldSelector"] = []string{"spec.nodeName=node"}
|
||||||
|
if !reflect.DeepEqual(get_params, values) {
|
||||||
|
t.Fatalf("%s: expected:\n%v\nsaw:\n%v\n", test.description, get_params, values)
|
||||||
|
}
|
||||||
|
return &http.Response{StatusCode: 200, Body: objBody(codec, &api.PodList{Items: test.pods})}, nil
|
||||||
|
case m.isFor("GET", "/replicationcontrollers"):
|
||||||
|
return &http.Response{StatusCode: 200, Body: objBody(codec, &api.ReplicationControllerList{Items: test.rcs})}, nil
|
||||||
|
case m.isFor("PUT", "/nodes/node"):
|
||||||
|
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 !reflect.DeepEqual(test.expected.Spec, new_node.Spec) {
|
||||||
|
t.Fatalf("%s: expected:\n%v\nsaw:\n%v\n", test.description, test.expected.Spec, new_node.Spec)
|
||||||
|
}
|
||||||
|
return &http.Response{StatusCode: 200, Body: objBody(codec, new_node)}, nil
|
||||||
|
case m.isFor("DELETE", "/namespaces/default/pods/bar"):
|
||||||
|
deleted = true
|
||||||
|
return &http.Response{StatusCode: 204, Body: objBody(codec, &test.pods[0])}, nil
|
||||||
|
default:
|
||||||
|
t.Fatalf("%s: unexpected request: %v %#v\n%#v", test.description, req.Method, req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
tf.ClientConfig = &client.Config{GroupVersion: testapi.Default.GroupVersion()}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
cmd := NewCmdDrain(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.expectDelete {
|
||||||
|
if !deleted {
|
||||||
|
t.Fatalf("%s: pod never deleted", test.description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !test.expectDelete {
|
||||||
|
if deleted {
|
||||||
|
t.Fatalf("%s: unexpected delete", test.description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyReq struct {
|
||||||
|
Request *http.Request
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyReq) isFor(method string, path string) bool {
|
||||||
|
req := m.Request
|
||||||
|
|
||||||
|
return method == req.Method && (req.URL.Path == path || req.URL.Path == strings.Join([]string{"/api/v1", path}, "") || req.URL.Path == strings.Join([]string{"/apis/extensions/v1beta1", path}, ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
func refJson(t *testing.T, o runtime.Object) string {
|
||||||
|
ref, err := api.GetReference(o)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, codec := NewAPIFactory()
|
||||||
|
json, err := codec.Encode(&api.SerializedReference{Reference: *ref})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(json)
|
||||||
|
}
|
|
@ -673,3 +673,10 @@ func (f *Factory) NilClientMapperForCommand() resource.ClientMapper {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// One stop shopping for a Builder
|
||||||
|
func (f *Factory) NewBuilder() *resource.Builder {
|
||||||
|
mapper, typer := f.Object()
|
||||||
|
|
||||||
|
return resource.NewBuilder(mapper, typer, f.ClientMapperForCommand())
|
||||||
|
}
|
||||||
|
|
|
@ -76,6 +76,12 @@ func BehaviorOnFatal(f func(string)) {
|
||||||
fatalErrHandler = f
|
fatalErrHandler = f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DefaultBehaviorOnFatal allows you to undo any previous override. Useful in
|
||||||
|
// tests.
|
||||||
|
func DefaultBehaviorOnFatal() {
|
||||||
|
fatalErrHandler = fatal
|
||||||
|
}
|
||||||
|
|
||||||
// fatal prints the message and then exits. If V(2) or greater, glog.Fatal
|
// fatal prints the message and then exits. If V(2) or greater, glog.Fatal
|
||||||
// is invoked for extended information.
|
// is invoked for extended information.
|
||||||
func fatal(msg string) {
|
func fatal(msg string) {
|
||||||
|
|
Loading…
Reference in New Issue