Add kubectl rollout undo

pull/6/head
Janet Kuo 2016-01-19 14:50:03 -08:00
parent cacda7bc7a
commit 442c75045a
19 changed files with 618 additions and 16 deletions

View File

@ -46,6 +46,7 @@ docs/man/man1/kubectl-rolling-update.1
docs/man/man1/kubectl-rollout-history.1
docs/man/man1/kubectl-rollout-pause.1
docs/man/man1/kubectl-rollout-resume.1
docs/man/man1/kubectl-rollout-undo.1
docs/man/man1/kubectl-rollout.1
docs/man/man1/kubectl-run.1
docs/man/man1/kubectl-scale.1
@ -96,6 +97,7 @@ docs/user-guide/kubectl/kubectl_rollout.md
docs/user-guide/kubectl/kubectl_rollout_history.md
docs/user-guide/kubectl/kubectl_rollout_pause.md
docs/user-guide/kubectl/kubectl_rollout_resume.md
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_uncordon.md

View File

@ -1792,6 +1792,52 @@ _kubectl_rollout_resume()
must_have_one_noun=()
}
_kubectl_rollout_undo()
{
last_command="kubectl_rollout_undo"
commands=()
flags=()
two_word_flags=()
flags_with_completion=()
flags_completion=()
flags+=("--filename=")
flags_with_completion+=("--filename")
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
two_word_flags+=("-f")
flags_with_completion+=("-f")
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
flags+=("--to-revision=")
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_rollout()
{
last_command="kubectl_rollout"
@ -1799,6 +1845,7 @@ _kubectl_rollout()
commands+=("history")
commands+=("pause")
commands+=("resume")
commands+=("undo")
flags=()
two_word_flags=()

View File

@ -0,0 +1,142 @@
.TH "KUBERNETES" "1" " kubernetes User Manuals" "Eric Paris" "Jan 2015" ""
.SH NAME
.PP
kubectl rollout undo \- undoes a previous rollout
.SH SYNOPSIS
.PP
\fBkubectl rollout undo\fP [OPTIONS]
.SH DESCRIPTION
.PP
undo rolls back to a previous rollout.
.SH OPTIONS
.PP
\fB\-f\fP, \fB\-\-filename\fP=[]
Filename, directory, or URL to a file identifying the resource to get from a server.
.PP
\fB\-\-to\-revision\fP=0
The revision to rollback to. Default to 0 (last revision).
.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
# Rollback to the previous deployment
$ kubectl rollout undo deployment/abc
.fi
.RE
.SH SEE ALSO
.PP
\fBkubectl\-rollout(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!

View File

@ -13,7 +13,7 @@ kubectl rollout \- rollout manages a deployment
.SH DESCRIPTION
.PP
rollout manages a deployment using subcommands
rollout manages a deployment using subcommands like "kubectl rollout undo deployment/abc"
.SH OPTIONS INHERITED FROM PARENT COMMANDS
@ -110,9 +110,21 @@ rollout manages a deployment using subcommands
comma\-separated list of pattern=N settings for file\-filtered logging
.SH EXAMPLE
.PP
.RS
.nf
# Rollback to the previous deployment
$ kubectl rollout undo deployment/abc
.fi
.RE
.SH SEE ALSO
.PP
\fBkubectl(1)\fP, \fBkubectl\-rollout\-history(1)\fP, \fBkubectl\-rollout\-pause(1)\fP, \fBkubectl\-rollout\-resume(1)\fP,
\fBkubectl(1)\fP, \fBkubectl\-rollout\-history(1)\fP, \fBkubectl\-rollout\-pause(1)\fP, \fBkubectl\-rollout\-resume(1)\fP, \fBkubectl\-rollout\-undo(1)\fP,
.SH HISTORY

View File

@ -109,7 +109,7 @@ kubectl
* [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 20-Jan-2016
###### Auto generated by spf13/cobra on 19-Jan-2016
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl.md?pixel)]()

View File

@ -34,12 +34,19 @@ rollout manages a deployment
### Synopsis
rollout manages a deployment using subcommands
rollout manages a deployment using subcommands like "kubectl rollout undo deployment/abc"
```
kubectl rollout SUBCOMMAND
```
### Examples
```
# Rollback to the previous deployment
$ kubectl rollout undo deployment/abc
```
### Options inherited from parent commands
```
@ -74,6 +81,7 @@ kubectl rollout SUBCOMMAND
* [kubectl rollout history](kubectl_rollout_history.md) - view rollout history
* [kubectl rollout pause](kubectl_rollout_pause.md) - Mark the provided resource as paused
* [kubectl rollout resume](kubectl_rollout_resume.md) - Resume a paused resource
* [kubectl rollout undo](kubectl_rollout_undo.md) - undoes a previous rollout
###### Auto generated by spf13/cobra on 2-Feb-2016

View File

@ -91,7 +91,7 @@ $ kubectl rollout pause deployment/nginx
* [kubectl rollout](kubectl_rollout.md) - rollout manages a deployment
###### Auto generated by spf13/cobra on 25-Jan-2016
###### Auto generated by spf13/cobra on 2-Feb-2016
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_rollout_pause.md?pixel)]()

View File

@ -89,7 +89,7 @@ $ kubectl rollout resume deployment/nginx
* [kubectl rollout](kubectl_rollout.md) - rollout manages a deployment
###### Auto generated by spf13/cobra on 25-Jan-2016
###### Auto generated by spf13/cobra on 2-Feb-2016
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_rollout_resume.md?pixel)]()

View File

@ -0,0 +1,93 @@
<!-- 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 rollout undo
undoes a previous rollout
### Synopsis
undo rolls back to a previous rollout.
```
kubectl rollout undo (TYPE NAME | TYPE/NAME) [flags]
```
### Examples
```
# Rollback to the previous deployment
$ kubectl rollout undo deployment/abc
```
### Options
```
-f, --filename=[]: Filename, directory, or URL to a file identifying the resource to get from a server.
--to-revision=0: The revision to rollback to. Default to 0 (last revision).
```
### 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 rollout](kubectl_rollout.md) - rollout manages a deployment
###### Auto generated by spf13/cobra on 29-Jan-2016
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_rollout_undo.md?pixel)]()
<!-- END MUNGE: GENERATED_ANALYTICS -->

View File

@ -242,6 +242,7 @@ runTests() {
deployment_replicas=".spec.replicas"
secret_data=".data"
secret_type=".type"
deployment_image_field="(index .spec.template.spec.containers 0).image"
# Passing no arguments to create is an error
! kubectl create
@ -1081,6 +1082,35 @@ __EOF__
kubectl delete deployment nginx-deployment "${kube_flags[@]}"
kubectl delete rc -l deployment.kubernetes.io/podTemplateHash "${kube_flags[@]}"
### Rollback a deployment
# Pre-condition: no deployment exists
kube::test::get_object_assert deployment "{{range.items}}{{$id_field}}:{{end}}" ''
# Command
# Create a deployment (revision 1)
kubectl create -f examples/extensions/deployment.yaml "${kube_flags[@]}"
kube::test::get_object_assert deployment "{{range.items}}{{$id_field}}:{{end}}" 'nginx-deployment:'
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_image_field}}:{{end}}" 'nginx:'
# Rollback to revision 1 - should be no-op
kubectl rollout undo deployment nginx-deployment --to-revision=1 "${kube_flags[@]}"
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_image_field}}:{{end}}" 'nginx:'
# Update the deployment (revision 2)
kubectl apply -f hack/testdata/deployment-revision2.yaml "${kube_flags[@]}"
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_image_field}}:{{end}}" 'nginx:latest:'
# Rollback to revision 1
kubectl rollout undo deployment nginx-deployment --to-revision=1 "${kube_flags[@]}"
sleep 1
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_image_field}}:{{end}}" 'nginx:'
# Rollback to revision 1000000 - should be no-op
kubectl rollout undo deployment nginx-deployment --to-revision=1000000 "${kube_flags[@]}"
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_image_field}}:{{end}}" 'nginx:'
# Rollback to last revision
kubectl rollout undo deployment nginx-deployment "${kube_flags[@]}"
sleep 1
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_image_field}}:{{end}}" 'nginx:latest:'
# Clean up
kubectl delete deployment nginx-deployment "${kube_flags[@]}"
kubectl delete rc -l deployment.kubernetes.io/podTemplateHash "${kube_flags[@]}"
######################
# ConfigMap #
######################

20
hack/testdata/deployment-revision2.yaml vendored Normal file
View File

@ -0,0 +1,20 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
labels:
name: nginx-deployment
spec:
replicas: 3
selector:
name: nginx
template:
metadata:
labels:
name: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80

View File

@ -347,6 +347,7 @@ tcp-services
terminated-pod-gc-threshold
tls-cert-file
tls-private-key-file
to-version
token-auth-file
ttl-secs
type-src

View File

@ -446,7 +446,7 @@ func (dc *DeploymentController) rollback(deployment *extensions.Deployment, toRe
if *toRevision == 0 {
if *toRevision = lastRevision(allRCs); *toRevision == 0 {
// If we still can't find the last revision, gives up rollback
dc.emitRollbackWarningEvent(deployment, "DeploymentRollbackRevisionNotFound", "Unable to find last revision.")
dc.emitRollbackWarningEvent(deployment, deploymentutil.RollbackRevisionNotFound, "Unable to find last revision.")
// Gives up rollback
return dc.updateDeploymentAndClearRollbackTo(deployment)
}
@ -468,7 +468,7 @@ func (dc *DeploymentController) rollback(deployment *extensions.Deployment, toRe
return deployment, err
}
}
dc.emitRollbackWarningEvent(deployment, "DeploymentRollbackRevisionNotFound", "Unable to find the revision to rollback to.")
dc.emitRollbackWarningEvent(deployment, deploymentutil.RollbackRevisionNotFound, "Unable to find the revision to rollback to.")
// Gives up rollback
return dc.updateDeploymentAndClearRollbackTo(deployment)
}
@ -478,7 +478,7 @@ func (dc *DeploymentController) emitRollbackWarningEvent(deployment *extensions.
}
func (dc *DeploymentController) emitRollbackNormalEvent(deployment *extensions.Deployment, message string) {
dc.eventRecorder.Eventf(deployment, api.EventTypeNormal, "DeploymentRollback", message)
dc.eventRecorder.Eventf(deployment, api.EventTypeNormal, deploymentutil.RollbackDone, message)
}
// updateDeploymentAndClearRollbackTo sets .spec.rollbackTo to nil and update the input deployment
@ -966,7 +966,7 @@ func (dc *DeploymentController) rollbackToTemplate(deployment *extensions.Deploy
performedRollback = true
} else {
glog.V(4).Infof("Rolling back to a revision that contains the same template as current deployment %s, skipping rollback...", deployment.Name)
dc.emitRollbackWarningEvent(deployment, "DeploymentRollbackTemplateUnchanged", fmt.Sprintf("The rollback revision contains the same template as current deployment %q", deployment.Name))
dc.emitRollbackWarningEvent(deployment, deploymentutil.RollbackTemplateUnchanged, fmt.Sprintf("The rollback revision contains the same template as current deployment %q", deployment.Name))
}
d, err = dc.updateDeploymentAndClearRollbackTo(deployment)
return

View File

@ -24,8 +24,9 @@ import (
)
const (
rollout_long = `rollout manages a deployment using subcommands`
rollout_example = ``
rollout_long = `rollout manages a deployment using subcommands like "kubectl rollout undo deployment/abc"`
rollout_example = `# Rollback to the previous deployment
$ kubectl rollout undo deployment/abc`
rollout_valid_resources = `Valid resource types include:
* deployments
`
@ -43,9 +44,11 @@ func NewCmdRollout(f *cmdutil.Factory, out io.Writer) *cobra.Command {
},
}
// subcommands
cmd.AddCommand(NewCmdRolloutHistory(f, out))
cmd.AddCommand(NewCmdRolloutPause(f, out))
cmd.AddCommand(NewCmdRolloutResume(f, out))
cmd.AddCommand(NewCmdRolloutUndo(f, out))
return cmd
}

View File

@ -0,0 +1,111 @@
/*
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 rollout
import (
"fmt"
"io"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
"github.com/spf13/cobra"
)
// UndoOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of
// referencing the cmd.Flags()
type UndoOptions struct {
Rollbacker kubectl.Rollbacker
Mapper meta.RESTMapper
Typer runtime.ObjectTyper
Info *resource.Info
ToRevision int64
Out io.Writer
Filenames []string
}
const (
undo_long = `undo rolls back to a previous rollout.`
undo_example = `# Rollback to the previous deployment
$ kubectl rollout undo deployment/abc`
)
func NewCmdRolloutUndo(f *cmdutil.Factory, out io.Writer) *cobra.Command {
options := &UndoOptions{}
cmd := &cobra.Command{
Use: "undo (TYPE NAME | TYPE/NAME) [flags]",
Short: "undoes a previous rollout",
Long: undo_long,
Example: undo_example,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.CompleteUndo(f, cmd, out, args))
cmdutil.CheckErr(options.RunUndo())
},
}
cmd.Flags().Int64("to-revision", 0, "The revision to rollback to. Default to 0 (last revision).")
usage := "Filename, directory, or URL to a file identifying the resource to get from a server."
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
return cmd
}
func (o *UndoOptions) CompleteUndo(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []string) error {
if len(args) == 0 && len(o.Filenames) == 0 {
return cmdutil.UsageError(cmd, "Required resource not specified.")
}
o.ToRevision = cmdutil.GetFlagInt64(cmd, "to-revision")
o.Mapper, o.Typer = f.Object()
o.Out = out
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
infos, err := resource.NewBuilder(o.Mapper, o.Typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, o.Filenames...).
ResourceTypeOrNameArgs(true, args...).
Latest().
Flatten().
Do().
Infos()
if err != nil {
return err
}
if len(infos) != 1 {
return fmt.Errorf("rollout undo is only supported on individual resources - %d resources were found", len(infos))
}
o.Info = infos[0]
o.Rollbacker, err = f.Rollbacker(o.Info.ResourceMapping())
return err
}
func (o *UndoOptions) RunUndo() error {
result, err := o.Rollbacker.Rollback(o.Info.Namespace, o.Info.Name, nil, o.ToRevision, o.Info.Object)
if err != nil {
return err
}
cmdutil.PrintSuccess(o.Mapper, false, o.Out, o.Info.Mapping.Resource, o.Info.Name, result)
return nil
}

View File

@ -88,6 +88,8 @@ type Factory struct {
Reaper func(mapping *meta.RESTMapping) (kubectl.Reaper, error)
// Returns a HistoryViewer for viewing change history
HistoryViewer func(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error)
// Returns a Rollbacker for changing the rollback version of the specified RESTMapping type or an error
Rollbacker func(mapping *meta.RESTMapping) (kubectl.Rollbacker, error)
// PodSelectorForObject returns the pod selector associated with the provided object
PodSelectorForObject func(object runtime.Object) (string, error)
// PortsForObject returns the ports associated with the provided object
@ -373,6 +375,14 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
}
return kubectl.HistoryViewerFor(mapping.GroupVersionKind.GroupKind(), clientset)
},
Rollbacker: func(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) {
mappingVersion := mapping.GroupVersionKind.GroupVersion()
client, err := clients.ClientForVersion(&mappingVersion)
if err != nil {
return nil, err
}
return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), client)
},
Validator: func(validate bool, cacheDir string) (validation.Schema, error) {
if validate {
client, err := clients.ClientForVersion(nil)

118
pkg/kubectl/rollback.go Normal file
View File

@ -0,0 +1,118 @@
/*
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 kubectl
import (
"fmt"
"os"
"os/signal"
"syscall"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/runtime"
deploymentutil "k8s.io/kubernetes/pkg/util/deployment"
"k8s.io/kubernetes/pkg/watch"
)
// Rollbacker provides an interface for resources that can be rolled back.
type Rollbacker interface {
Rollback(namespace, name string, updatedAnnotations map[string]string, toRevision int64, obj runtime.Object) (string, error)
}
func RollbackerFor(kind unversioned.GroupKind, c client.Interface) (Rollbacker, error) {
switch kind {
case extensions.Kind("Deployment"):
return &DeploymentRollbacker{c}, nil
}
return nil, fmt.Errorf("no rollbacker has been implemented for %q", kind)
}
type DeploymentRollbacker struct {
c client.Interface
}
func (r *DeploymentRollbacker) Rollback(namespace, name string, updatedAnnotations map[string]string, toRevision int64, obj runtime.Object) (string, error) {
deploymentRollback := &extensions.DeploymentRollback{
Name: name,
UpdatedAnnotations: updatedAnnotations,
RollbackTo: extensions.RollbackConfig{
Revision: toRevision,
},
}
result := ""
// Get current events
events, err := r.c.Events(namespace).List(api.ListOptions{})
if err != nil {
return result, err
}
// Do the rollback
if err := r.c.Extensions().Deployments(namespace).Rollback(deploymentRollback); err != nil {
return result, err
}
// Watch for the changes of events
watch, err := r.c.Events(namespace).Watch(api.ListOptions{Watch: true, ResourceVersion: events.ResourceVersion})
if err != nil {
return result, err
}
result = watchRollbackEvent(watch)
return result, err
}
// watchRollbackEvent watches for rollback events and returns rollback result
func watchRollbackEvent(w watch.Interface) string {
signals := make(chan os.Signal, 1)
signal.Notify(signals, os.Interrupt, os.Kill, syscall.SIGTERM)
for {
select {
case event, ok := <-w.ResultChan():
if !ok {
return ""
}
obj, ok := event.Object.(*api.Event)
if !ok {
w.Stop()
return ""
}
isRollback, result := isRollbackEvent(obj)
if isRollback {
w.Stop()
return result
}
case <-signals:
w.Stop()
}
}
}
// isRollbackEvent checks if the input event is about rollback, and returns true and
// related result string back if it is.
func isRollbackEvent(e *api.Event) (bool, string) {
rollbackEventReasons := []string{deploymentutil.RollbackRevisionNotFound, deploymentutil.RollbackTemplateUnchanged, deploymentutil.RollbackDone}
for _, reason := range rollbackEventReasons {
if e.Reason == reason {
if reason == deploymentutil.RollbackDone {
return true, "rolled back"
}
return true, fmt.Sprintf("skipped rollback (%s: %s)", e.Reason, e.Message)
}
}
return false, ""
}

View File

@ -32,6 +32,11 @@ import (
const (
// The revision annotation of a deployment's replication controllers which records its rollout sequence
RevisionAnnotation = "deployment.kubernetes.io/revision"
// Here are the possible rollback event reasons
RollbackRevisionNotFound = "DeploymentRollbackRevisionNotFound"
RollbackTemplateUnchanged = "DeploymentRollbackTemplateUnchanged"
RollbackDone = "DeploymentRollback"
)
// GetOldRCs returns the old RCs targeted by the given Deployment; get PodList and RCList from client interface.

View File

@ -770,7 +770,7 @@ func testRollbackDeploymentRCNoRevision(f *Framework) {
waitForEvents(unversionedClient, ns, deployment, 2)
events, err := c.Events(ns).Search(deployment)
Expect(err).NotTo(HaveOccurred())
Expect(events.Items[1].Reason).Should(Equal("DeploymentRollbackRevisionNotFound"))
Expect(events.Items[1].Reason).Should(Equal(deploymentutil.RollbackRevisionNotFound))
// Check if it's still revision 1
checkDeploymentRevision(c, ns, deploymentName, "1", deploymentImageName, deploymentImage)
@ -804,7 +804,7 @@ func testRollbackDeploymentRCNoRevision(f *Framework) {
waitForEvents(unversionedClient, ns, deployment, 5)
events, err = c.Events(ns).Search(deployment)
Expect(err).NotTo(HaveOccurred())
Expect(events.Items[4].Reason).Should(Equal("DeploymentRollback"))
Expect(events.Items[4].Reason).Should(Equal(deploymentutil.RollbackDone))
// Check if it's updated to revision 3 correctly
checkDeploymentRevision(c, ns, deploymentName, "3", deploymentImageName, deploymentImage)
@ -821,7 +821,7 @@ func testRollbackDeploymentRCNoRevision(f *Framework) {
waitForEvents(unversionedClient, ns, deployment, 7)
events, err = c.Events(ns).Search(deployment)
Expect(err).NotTo(HaveOccurred())
Expect(events.Items[6].Reason).Should(Equal("DeploymentRollbackRevisionNotFound"))
Expect(events.Items[6].Reason).Should(Equal(deploymentutil.RollbackRevisionNotFound))
// Check if it's still revision 3
checkDeploymentRevision(c, ns, deploymentName, "3", deploymentImageName, deploymentImage)
@ -838,7 +838,7 @@ func testRollbackDeploymentRCNoRevision(f *Framework) {
waitForEvents(unversionedClient, ns, deployment, 8)
events, err = c.Events(ns).Search(deployment)
Expect(err).NotTo(HaveOccurred())
Expect(events.Items[7].Reason).Should(Equal("DeploymentRollbackTemplateUnchanged"))
Expect(events.Items[7].Reason).Should(Equal(deploymentutil.RollbackTemplateUnchanged))
// Check if it's still revision 3
checkDeploymentRevision(c, ns, deploymentName, "3", deploymentImageName, deploymentImage)