Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kubectl: refactor rollout history to be more configurable #27267

Merged
merged 1 commit into from
Jul 12, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 8 additions & 19 deletions pkg/kubectl/cmd/rollout/rollout_history.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func RunHistory(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []st
if len(args) == 0 && len(options.Filenames) == 0 {
return cmdutil.UsageError(cmd, "Required resource not specified.")
}
revisionDetail := cmdutil.GetFlagInt64(cmd, "revision")
revision := cmdutil.GetFlagInt64(cmd, "revision")

mapper, typer := f.Object(false)

Expand All @@ -92,7 +92,7 @@ func RunHistory(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []st
return err
}

err = r.Visit(func(info *resource.Info, err error) error {
return r.Visit(func(info *resource.Info, err error) error {
if err != nil {
return err
}
Expand All @@ -101,28 +101,17 @@ func RunHistory(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []st
if err != nil {
return err
}
historyInfo, err := historyViewer.History(info.Namespace, info.Name)
historyInfo, err := historyViewer.ViewHistory(info.Namespace, info.Name, revision)
if err != nil {
return err
}

if revisionDetail > 0 {
// Print details of a specific revision
template, ok := historyInfo.RevisionToTemplate[revisionDetail]
if !ok {
return fmt.Errorf("unable to find revision %d of %s %q", revisionDetail, mapping.Resource, info.Name)
}
fmt.Fprintf(out, "%s %q revision %d\n", mapping.Resource, info.Name, revisionDetail)
kubectl.DescribePodTemplate(template, out)
} else {
// Print all revisions
formattedOutput, printErr := kubectl.PrintRolloutHistory(historyInfo, mapping.Resource, info.Name)
if printErr != nil {
return printErr
}
fmt.Fprintf(out, "%s\n", formattedOutput)
header := fmt.Sprintf("%s %q", mapping.Resource, info.Name)
if revision > 0 {
header = fmt.Sprintf("%s with revision #%d", header, revision)
}
fmt.Fprintf(out, "%s\n", header)
fmt.Fprintf(out, "%s\n", historyInfo)
return nil
})
return err
}
59 changes: 30 additions & 29 deletions pkg/kubectl/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package kubectl

import (
"bytes"
"fmt"
"io"

Expand All @@ -34,9 +35,9 @@ const (
ChangeCauseAnnotation = "kubernetes.io/change-cause"
)

// HistoryViewer provides an interface for resources that can be rolled back.
// HistoryViewer provides an interface for resources have historical information.
type HistoryViewer interface {
History(namespace, name string) (HistoryInfo, error)
ViewHistory(namespace, name string, revision int64) (string, error)
}

func HistoryViewerFor(kind unversioned.GroupKind, c clientset.Interface) (HistoryViewer, error) {
Expand All @@ -47,68 +48,68 @@ func HistoryViewerFor(kind unversioned.GroupKind, c clientset.Interface) (Histor
return nil, fmt.Errorf("no history viewer has been implemented for %q", kind)
}

// HistoryInfo stores the mapping from revision to podTemplate;
// note that change-cause annotation should be copied to podTemplate
type HistoryInfo struct {
RevisionToTemplate map[int64]*api.PodTemplateSpec
}

type DeploymentHistoryViewer struct {
c clientset.Interface
}

// History returns a revision-to-replicaset map as the revision history of a deployment
func (h *DeploymentHistoryViewer) History(namespace, name string) (HistoryInfo, error) {
historyInfo := HistoryInfo{
RevisionToTemplate: make(map[int64]*api.PodTemplateSpec),
}
// ViewHistory returns a revision-to-replicaset map as the revision history of a deployment
func (h *DeploymentHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) {
deployment, err := h.c.Extensions().Deployments(namespace).Get(name)
if err != nil {
return historyInfo, fmt.Errorf("failed to retrieve deployment %s: %v", name, err)
return "", fmt.Errorf("failed to retrieve deployment %s: %v", name, err)
}
_, allOldRSs, newRS, err := deploymentutil.GetAllReplicaSets(deployment, h.c)
if err != nil {
return historyInfo, fmt.Errorf("failed to retrieve replica sets from deployment %s: %v", name, err)
return "", fmt.Errorf("failed to retrieve replica sets from deployment %s: %v", name, err)
}
allRSs := allOldRSs
if newRS != nil {
allRSs = append(allRSs, newRS)
}

historyInfo := make(map[int64]*api.PodTemplateSpec)
for _, rs := range allRSs {
v, err := deploymentutil.Revision(rs)
if err != nil {
continue
}
historyInfo.RevisionToTemplate[v] = &rs.Spec.Template
historyInfo[v] = &rs.Spec.Template
changeCause := getChangeCause(rs)
if historyInfo.RevisionToTemplate[v].Annotations == nil {
historyInfo.RevisionToTemplate[v].Annotations = make(map[string]string)
if historyInfo[v].Annotations == nil {
historyInfo[v].Annotations = make(map[string]string)
}
if len(changeCause) > 0 {
historyInfo.RevisionToTemplate[v].Annotations[ChangeCauseAnnotation] = changeCause
historyInfo[v].Annotations[ChangeCauseAnnotation] = changeCause
}
}
return historyInfo, nil
}

// PrintRolloutHistory prints a formatted table of the input revision history of the deployment
func PrintRolloutHistory(historyInfo HistoryInfo, resource, name string) (string, error) {
if len(historyInfo.RevisionToTemplate) == 0 {
return fmt.Sprintf("No rollout history found in %s %q", resource, name), nil
if len(historyInfo) == 0 {
return "No rollout history found.", nil
}

if revision > 0 {
// Print details of a specific revision
template, ok := historyInfo[revision]
if !ok {
return "", fmt.Errorf("unable to find the specified revision")
}
buf := bytes.NewBuffer([]byte{})
DescribePodTemplate(template, buf)
return buf.String(), nil
}

// Sort the revisionToChangeCause map by revision
revisions := make([]int64, 0, len(historyInfo.RevisionToTemplate))
for r := range historyInfo.RevisionToTemplate {
revisions := make([]int64, 0, len(historyInfo))
for r := range historyInfo {
revisions = append(revisions, r)
}
sliceutil.SortInts64(revisions)

return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "%s %q:\n", resource, name)
fmt.Fprintf(out, "REVISION\tCHANGE-CAUSE\n")
for _, r := range revisions {
// Find the change-cause of revision r
changeCause := historyInfo.RevisionToTemplate[r].Annotations[ChangeCauseAnnotation]
changeCause := historyInfo[r].Annotations[ChangeCauseAnnotation]
if len(changeCause) == 0 {
changeCause = "<none>"
}
Expand Down