mirror of https://github.com/k3s-io/k3s
rollout undo add dry-run implementation
parent
85c91eb332
commit
6a68dbdac4
|
@ -1931,6 +1931,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
|
||||
|
|
|
@ -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 {
|
||||
|
@ -80,6 +84,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
|
||||
}
|
||||
|
@ -92,6 +97,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 {
|
||||
|
@ -129,7 +135,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
|
||||
|
|
|
@ -631,10 +631,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) {
|
||||
|
@ -643,7 +643,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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue