mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 05:40:42 +00:00 
			
		
		
		
	kubectl: refactor rollout history to be more configurable
ChangeCauseAnnotation is hardcoded in PrintRolloutHistory and it needs to be overriden since other resources that may need to be added in `kubectl rollout history` may not use it. Instead of adding one more method in the factory, refactor the existing HistoryViewer interface to accomodate the change.
This commit is contained in:
		| @@ -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 { | 	if len(args) == 0 && len(options.Filenames) == 0 { | ||||||
| 		return cmdutil.UsageError(cmd, "Required resource not specified.") | 		return cmdutil.UsageError(cmd, "Required resource not specified.") | ||||||
| 	} | 	} | ||||||
| 	revisionDetail := cmdutil.GetFlagInt64(cmd, "revision") | 	revision := cmdutil.GetFlagInt64(cmd, "revision") | ||||||
|  |  | ||||||
| 	mapper, typer := f.Object(false) | 	mapper, typer := f.Object(false) | ||||||
|  |  | ||||||
| @@ -92,7 +92,7 @@ func RunHistory(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []st | |||||||
| 		return err | 		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 { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| @@ -101,28 +101,17 @@ func RunHistory(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []st | |||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 		historyInfo, err := historyViewer.History(info.Namespace, info.Name) | 		historyInfo, err := historyViewer.ViewHistory(info.Namespace, info.Name, revision) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if revisionDetail > 0 { | 		header := fmt.Sprintf("%s %q", mapping.Resource, info.Name) | ||||||
| 			// Print details of a specific revision | 		if revision > 0 { | ||||||
| 			template, ok := historyInfo.RevisionToTemplate[revisionDetail] | 			header = fmt.Sprintf("%s with revision #%d", header, revision) | ||||||
| 			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) |  | ||||||
| 		} | 		} | ||||||
|  | 		fmt.Fprintf(out, "%s\n", header) | ||||||
|  | 		fmt.Fprintf(out, "%s\n", historyInfo) | ||||||
| 		return nil | 		return nil | ||||||
| 	}) | 	}) | ||||||
| 	return err |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ limitations under the License. | |||||||
| package kubectl | package kubectl | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"bytes" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
|  |  | ||||||
| @@ -34,9 +35,9 @@ const ( | |||||||
| 	ChangeCauseAnnotation = "kubernetes.io/change-cause" | 	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 { | 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) { | func HistoryViewerFor(kind unversioned.GroupKind, c clientset.Interface) (HistoryViewer, error) { | ||||||
| @@ -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) | 	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 { | type DeploymentHistoryViewer struct { | ||||||
| 	c clientset.Interface | 	c clientset.Interface | ||||||
| } | } | ||||||
|  |  | ||||||
| // History returns a revision-to-replicaset map as the revision history of a deployment | // ViewHistory returns a revision-to-replicaset map as the revision history of a deployment | ||||||
| func (h *DeploymentHistoryViewer) History(namespace, name string) (HistoryInfo, error) { | func (h *DeploymentHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) { | ||||||
| 	historyInfo := HistoryInfo{ |  | ||||||
| 		RevisionToTemplate: make(map[int64]*api.PodTemplateSpec), |  | ||||||
| 	} |  | ||||||
| 	deployment, err := h.c.Extensions().Deployments(namespace).Get(name) | 	deployment, err := h.c.Extensions().Deployments(namespace).Get(name) | ||||||
| 	if err != nil { | 	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) | 	_, allOldRSs, newRS, err := deploymentutil.GetAllReplicaSets(deployment, h.c) | ||||||
| 	if err != nil { | 	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 | 	allRSs := allOldRSs | ||||||
| 	if newRS != nil { | 	if newRS != nil { | ||||||
| 		allRSs = append(allRSs, newRS) | 		allRSs = append(allRSs, newRS) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	historyInfo := make(map[int64]*api.PodTemplateSpec) | ||||||
| 	for _, rs := range allRSs { | 	for _, rs := range allRSs { | ||||||
| 		v, err := deploymentutil.Revision(rs) | 		v, err := deploymentutil.Revision(rs) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		historyInfo.RevisionToTemplate[v] = &rs.Spec.Template | 		historyInfo[v] = &rs.Spec.Template | ||||||
| 		changeCause := getChangeCause(rs) | 		changeCause := getChangeCause(rs) | ||||||
| 		if historyInfo.RevisionToTemplate[v].Annotations == nil { | 		if historyInfo[v].Annotations == nil { | ||||||
| 			historyInfo.RevisionToTemplate[v].Annotations = make(map[string]string) | 			historyInfo[v].Annotations = make(map[string]string) | ||||||
| 		} | 		} | ||||||
| 		if len(changeCause) > 0 { | 		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 | 	if len(historyInfo) == 0 { | ||||||
| func PrintRolloutHistory(historyInfo HistoryInfo, resource, name string) (string, error) { | 		return "No rollout history found.", nil | ||||||
| 	if len(historyInfo.RevisionToTemplate) == 0 { |  | ||||||
| 		return fmt.Sprintf("No rollout history found in %s %q", resource, name), 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 | 	// Sort the revisionToChangeCause map by revision | ||||||
| 	revisions := make([]int64, 0, len(historyInfo.RevisionToTemplate)) | 	revisions := make([]int64, 0, len(historyInfo)) | ||||||
| 	for r := range historyInfo.RevisionToTemplate { | 	for r := range historyInfo { | ||||||
| 		revisions = append(revisions, r) | 		revisions = append(revisions, r) | ||||||
| 	} | 	} | ||||||
| 	sliceutil.SortInts64(revisions) | 	sliceutil.SortInts64(revisions) | ||||||
|  |  | ||||||
| 	return tabbedString(func(out io.Writer) error { | 	return tabbedString(func(out io.Writer) error { | ||||||
| 		fmt.Fprintf(out, "%s %q:\n", resource, name) |  | ||||||
| 		fmt.Fprintf(out, "REVISION\tCHANGE-CAUSE\n") | 		fmt.Fprintf(out, "REVISION\tCHANGE-CAUSE\n") | ||||||
| 		for _, r := range revisions { | 		for _, r := range revisions { | ||||||
| 			// Find the change-cause of revision r | 			// Find the change-cause of revision r | ||||||
| 			changeCause := historyInfo.RevisionToTemplate[r].Annotations[ChangeCauseAnnotation] | 			changeCause := historyInfo[r].Annotations[ChangeCauseAnnotation] | ||||||
| 			if len(changeCause) == 0 { | 			if len(changeCause) == 0 { | ||||||
| 				changeCause = "<none>" | 				changeCause = "<none>" | ||||||
| 			} | 			} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user