Merge pull request #31110 from AdoHe/rollout_dry_run

Automatic merge from submit-queue

Rollout dry run

**What this PR does / why we need it**: rollout undo add dry-run implementation

**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #28219

**Release note**:
<!--  Steps to write your release note:
1. Use the release-note-* labels to set the release note state (if you have access) 
2. Enter your extended release note in the below block; leaving it blank means using the PR title as the release note. If no release note is required, just write `NONE`. 
-->
```
rollout undo add dry-run option
```
pull/6/head
Kubernetes Submit Queue 2016-09-11 15:05:16 -07:00 committed by GitHub
commit 27c5e5dca7
4 changed files with 73 additions and 11 deletions

View File

@ -1967,6 +1967,9 @@ __EOF__
# Update the deployment (revision 2)
kubectl apply -f hack/testdata/deployment-revision2.yaml "${kube_flags[@]}"
kube::test::get_object_assert deployment.extensions "{{range.items}}{{$deployment_image_field}}:{{end}}" "${IMAGE_DEPLOYMENT_R2}:"
# Rollback to revision 1 with dry-run - should be no-op
kubectl rollout undo deployment nginx --to-revision=1 --dry-run=true "${kube_flags[@]}"
kube::test::get_object_assert deployment.extensions "{{range.items}}{{$deployment_image_field}}:{{end}}" "${IMAGE_DEPLOYMENT_R2}:"
# Rollback to revision 1
kubectl rollout undo deployment nginx --to-revision=1 "${kube_flags[@]}"
sleep 1

View File

@ -38,6 +38,7 @@ type UndoOptions struct {
Typer runtime.ObjectTyper
Infos []*resource.Info
ToRevision int64
DryRun bool
Out io.Writer
Filenames []string
@ -52,7 +53,10 @@ var (
kubectl rollout undo deployment/abc
# Rollback to deployment revision 3
kubectl rollout undo deployment/abc --to-revision=3`)
kubectl rollout undo deployment/abc --to-revision=3
# Rollback to the previous deployment with dry-run
kubectl rollout undo --dry-run=true deployment/abc`)
)
func NewCmdRolloutUndo(f *cmdutil.Factory, out io.Writer) *cobra.Command {
@ -85,6 +89,7 @@ func NewCmdRolloutUndo(f *cmdutil.Factory, out io.Writer) *cobra.Command {
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, &opts.Filenames, usage)
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddRecursiveFlag(cmd, &opts.Recursive)
return cmd
}
@ -97,6 +102,7 @@ func (o *UndoOptions) CompleteUndo(f *cmdutil.Factory, cmd *cobra.Command, out i
o.ToRevision = cmdutil.GetFlagInt64(cmd, "to-revision")
o.Mapper, o.Typer = f.Object(false)
o.Out = out
o.DryRun = cmdutil.GetFlagBool(cmd, "dry-run")
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
@ -134,7 +140,7 @@ func (o *UndoOptions) CompleteUndo(f *cmdutil.Factory, cmd *cobra.Command, out i
func (o *UndoOptions) RunUndo() error {
allErrs := []error{}
for ix, info := range o.Infos {
result, err := o.Rollbackers[ix].Rollback(info.Object, nil, o.ToRevision)
result, err := o.Rollbackers[ix].Rollback(info.Object, nil, o.ToRevision, o.DryRun)
if err != nil {
allErrs = append(allErrs, cmdutil.AddSourceToErr("undoing", info.Source, err))
continue

View File

@ -715,10 +715,10 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
HistoryViewer: func(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) {
mappingVersion := mapping.GroupVersionKind.GroupVersion()
client, err := clients.ClientForVersion(&mappingVersion)
clientset := clientset.FromUnversionedClient(client)
if err != nil {
return nil, err
}
clientset := clientset.FromUnversionedClient(client)
return kubectl.HistoryViewerFor(mapping.GroupVersionKind.GroupKind(), clientset)
},
Rollbacker: func(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) {
@ -727,7 +727,8 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
if err != nil {
return nil, err
}
return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), client)
clientset := clientset.FromUnversionedClient(client)
return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), clientset)
},
StatusViewer: func(mapping *meta.RESTMapping) (kubectl.StatusViewer, error) {
mappingVersion := mapping.GroupVersionKind.GroupVersion()

View File

@ -17,6 +17,7 @@ limitations under the License.
package kubectl
import (
"bytes"
"fmt"
"os"
"os/signal"
@ -25,18 +26,19 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
client "k8s.io/kubernetes/pkg/client/unversioned"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
"k8s.io/kubernetes/pkg/runtime"
sliceutil "k8s.io/kubernetes/pkg/util/slice"
"k8s.io/kubernetes/pkg/watch"
)
// Rollbacker provides an interface for resources that can be rolled back.
type Rollbacker interface {
Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64) (string, error)
Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64, dryRun bool) (string, error)
}
func RollbackerFor(kind unversioned.GroupKind, c client.Interface) (Rollbacker, error) {
func RollbackerFor(kind unversioned.GroupKind, c clientset.Interface) (Rollbacker, error) {
switch kind {
case extensions.Kind("Deployment"):
return &DeploymentRollbacker{c}, nil
@ -45,14 +47,17 @@ func RollbackerFor(kind unversioned.GroupKind, c client.Interface) (Rollbacker,
}
type DeploymentRollbacker struct {
c client.Interface
c clientset.Interface
}
func (r *DeploymentRollbacker) Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64) (string, error) {
func (r *DeploymentRollbacker) Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64, dryRun bool) (string, error) {
d, ok := obj.(*extensions.Deployment)
if !ok {
return "", fmt.Errorf("passed object is not a Deployment: %#v", obj)
}
if dryRun {
return simpleDryRun(d, r.c, toRevision)
}
if d.Spec.Paused {
return "", fmt.Errorf("you cannot rollback a paused deployment; resume it first with 'kubectl rollout resume deployment/%s' and try again", d.Name)
}
@ -66,7 +71,7 @@ func (r *DeploymentRollbacker) Rollback(obj runtime.Object, updatedAnnotations m
result := ""
// Get current events
events, err := r.c.Events(d.Namespace).List(api.ListOptions{})
events, err := r.c.Core().Events(d.Namespace).List(api.ListOptions{})
if err != nil {
return result, err
}
@ -75,7 +80,7 @@ func (r *DeploymentRollbacker) Rollback(obj runtime.Object, updatedAnnotations m
return result, err
}
// Watch for the changes of events
watch, err := r.c.Events(d.Namespace).Watch(api.ListOptions{Watch: true, ResourceVersion: events.ResourceVersion})
watch, err := r.c.Core().Events(d.Namespace).Watch(api.ListOptions{Watch: true, ResourceVersion: events.ResourceVersion})
if err != nil {
return result, err
}
@ -123,3 +128,50 @@ func isRollbackEvent(e *api.Event) (bool, string) {
}
return false, ""
}
func simpleDryRun(deployment *extensions.Deployment, c clientset.Interface, toRevision int64) (string, error) {
_, allOldRSs, newRS, err := deploymentutil.GetAllReplicaSets(deployment, c)
if err != nil {
return "", fmt.Errorf("failed to retrieve replica sets from deployment %s: %v", deployment.Name, err)
}
allRSs := allOldRSs
if newRS != nil {
allRSs = append(allRSs, newRS)
}
revisionToSpec := make(map[int64]*api.PodTemplateSpec)
for _, rs := range allRSs {
v, err := deploymentutil.Revision(rs)
if err != nil {
continue
}
revisionToSpec[v] = &rs.Spec.Template
}
if len(revisionToSpec) == 0 {
return "No rollout history found.", nil
}
if toRevision > 0 {
template, ok := revisionToSpec[toRevision]
if !ok {
return "", fmt.Errorf("unable to find specified revision")
}
buf := bytes.NewBuffer([]byte{})
DescribePodTemplate(template, buf)
return buf.String(), nil
}
// Sort the revisionToSpec map by revision
revisions := make([]int64, 0, len(revisionToSpec))
for r := range revisionToSpec {
revisions = append(revisions, r)
}
sliceutil.SortInts64(revisions)
template, _ := revisionToSpec[revisions[len(revisions)-1]]
buf := bytes.NewBuffer([]byte{})
buf.WriteString("\n")
DescribePodTemplate(template, buf)
return buf.String(), nil
}