1/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package rollout
18
19import (
20	"fmt"
21
22	"github.com/spf13/cobra"
23
24	"k8s.io/cli-runtime/pkg/genericclioptions"
25	"k8s.io/cli-runtime/pkg/printers"
26	"k8s.io/cli-runtime/pkg/resource"
27	cmdutil "k8s.io/kubectl/pkg/cmd/util"
28	"k8s.io/kubectl/pkg/polymorphichelpers"
29	"k8s.io/kubectl/pkg/scheme"
30	"k8s.io/kubectl/pkg/util/i18n"
31	"k8s.io/kubectl/pkg/util/templates"
32)
33
34var (
35	historyLong = templates.LongDesc(i18n.T(`
36		View previous rollout revisions and configurations.`))
37
38	historyExample = templates.Examples(`
39		# View the rollout history of a deployment
40		kubectl rollout history deployment/abc
41
42		# View the details of daemonset revision 3
43		kubectl rollout history daemonset/abc --revision=3`)
44)
45
46// RolloutHistoryOptions holds the options for 'rollout history' sub command
47type RolloutHistoryOptions struct {
48	PrintFlags *genericclioptions.PrintFlags
49	ToPrinter  func(string) (printers.ResourcePrinter, error)
50
51	Revision int64
52
53	Builder          func() *resource.Builder
54	Resources        []string
55	Namespace        string
56	EnforceNamespace bool
57
58	HistoryViewer    polymorphichelpers.HistoryViewerFunc
59	RESTClientGetter genericclioptions.RESTClientGetter
60
61	resource.FilenameOptions
62	genericclioptions.IOStreams
63}
64
65// NewRolloutHistoryOptions returns an initialized RolloutHistoryOptions instance
66func NewRolloutHistoryOptions(streams genericclioptions.IOStreams) *RolloutHistoryOptions {
67	return &RolloutHistoryOptions{
68		PrintFlags: genericclioptions.NewPrintFlags("").WithTypeSetter(scheme.Scheme),
69		IOStreams:  streams,
70	}
71}
72
73// NewCmdRolloutHistory returns a Command instance for RolloutHistory sub command
74func NewCmdRolloutHistory(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
75	o := NewRolloutHistoryOptions(streams)
76
77	validArgs := []string{"deployment", "daemonset", "statefulset"}
78
79	cmd := &cobra.Command{
80		Use:                   "history (TYPE NAME | TYPE/NAME) [flags]",
81		DisableFlagsInUseLine: true,
82		Short:                 i18n.T("View rollout history"),
83		Long:                  historyLong,
84		Example:               historyExample,
85		Run: func(cmd *cobra.Command, args []string) {
86			cmdutil.CheckErr(o.Complete(f, cmd, args))
87			cmdutil.CheckErr(o.Validate())
88			cmdutil.CheckErr(o.Run())
89		},
90		ValidArgs: validArgs,
91	}
92
93	cmd.Flags().Int64Var(&o.Revision, "revision", o.Revision, "See the details, including podTemplate of the revision specified")
94
95	usage := "identifying the resource to get from a server."
96	cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
97
98	o.PrintFlags.AddFlags(cmd)
99
100	return cmd
101}
102
103// Complete completes al the required options
104func (o *RolloutHistoryOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
105	o.Resources = args
106
107	var err error
108	if o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace(); err != nil {
109		return err
110	}
111
112	o.ToPrinter = func(operation string) (printers.ResourcePrinter, error) {
113		o.PrintFlags.NamePrintFlags.Operation = operation
114		return o.PrintFlags.ToPrinter()
115	}
116
117	o.HistoryViewer = polymorphichelpers.HistoryViewerFn
118	o.RESTClientGetter = f
119	o.Builder = f.NewBuilder
120
121	return nil
122}
123
124// Validate makes sure all the provided values for command-line options are valid
125func (o *RolloutHistoryOptions) Validate() error {
126	if len(o.Resources) == 0 && cmdutil.IsFilenameSliceEmpty(o.Filenames, o.Kustomize) {
127		return fmt.Errorf("required resource not specified")
128	}
129	if o.Revision < 0 {
130		return fmt.Errorf("revision must be a positive integer: %v", o.Revision)
131	}
132
133	return nil
134}
135
136// Run performs the execution of 'rollout history' sub command
137func (o *RolloutHistoryOptions) Run() error {
138
139	r := o.Builder().
140		WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
141		NamespaceParam(o.Namespace).DefaultNamespace().
142		FilenameParam(o.EnforceNamespace, &o.FilenameOptions).
143		ResourceTypeOrNameArgs(true, o.Resources...).
144		ContinueOnError().
145		Latest().
146		Flatten().
147		Do()
148	if err := r.Err(); err != nil {
149		return err
150	}
151
152	return r.Visit(func(info *resource.Info, err error) error {
153		if err != nil {
154			return err
155		}
156
157		mapping := info.ResourceMapping()
158		historyViewer, err := o.HistoryViewer(o.RESTClientGetter, mapping)
159		if err != nil {
160			return err
161		}
162		historyInfo, err := historyViewer.ViewHistory(info.Namespace, info.Name, o.Revision)
163		if err != nil {
164			return err
165		}
166
167		withRevision := ""
168		if o.Revision > 0 {
169			withRevision = fmt.Sprintf("with revision #%d", o.Revision)
170		}
171
172		printer, err := o.ToPrinter(fmt.Sprintf("%s\n%s", withRevision, historyInfo))
173		if err != nil {
174			return err
175		}
176
177		return printer.PrintObj(info.Object, o.Out)
178	})
179}
180