@@ -6,11 +6,13 @@ package status
66import (
77 "context"
88 "fmt"
9+ "strings"
910 "time"
1011
1112 "github.com/spf13/cobra"
1213 "k8s.io/cli-runtime/pkg/genericclioptions"
1314 cmdutil "k8s.io/kubectl/pkg/cmd/util"
15+ "k8s.io/kubectl/pkg/util/slice"
1416 "sigs.k8s.io/cli-utils/cmd/flagutils"
1517 "sigs.k8s.io/cli-utils/cmd/status/printers"
1618 "sigs.k8s.io/cli-utils/cmd/status/printers/printer"
@@ -24,18 +26,39 @@ import (
2426 "sigs.k8s.io/cli-utils/pkg/kstatus/status"
2527 "sigs.k8s.io/cli-utils/pkg/manifestreader"
2628 "sigs.k8s.io/cli-utils/pkg/object"
29+ printcommon "sigs.k8s.io/cli-utils/pkg/print/common"
30+ pkgprinters "sigs.k8s.io/cli-utils/pkg/printers"
2731)
2832
29- func GetRunner(factory cmdutil.Factory, invFactory inventory.ClientFactory, loader manifestreader.ManifestLoader) *Runner {
33+ const (
34+ Known = "known"
35+ Current = "current"
36+ Deleted = "deleted"
37+ Forever = "forever"
38+ )
39+
40+ const (
41+ Local = "local"
42+ Remote = "remote"
43+ )
44+
45+ var (
46+ PollUntilOptions = []string{Known, Current, Deleted, Forever}
47+ )
48+
49+ func GetRunner(ctx context.Context, factory cmdutil.Factory,
50+ invFactory inventory.ClientFactory, loader Loader) *Runner {
3051 r := &Runner{
52+ ctx: ctx,
3153 factory: factory,
3254 invFactory: invFactory,
33- loader: NewInventoryLoader( loader) ,
34- pollerFactoryFunc : pollerFactoryFunc,
55+ loader: loader,
56+ PollerFactoryFunc : pollerFactoryFunc,
3557 }
3658 c := &cobra.Command{
37- Use: "status (DIRECTORY | STDIN)",
38- RunE: r.runE,
59+ Use: "status (DIRECTORY | STDIN)",
60+ PreRunE: r.preRunE,
61+ RunE: r.runE,
3962 }
4063 c.Flags().DurationVar(&r.period, "poll-period", 2*time.Second,
4164 "Polling period for resource statuses.")
@@ -44,18 +67,24 @@ func GetRunner(factory cmdutil.Factory, invFactory inventory.ClientFactory, load
4467 c.Flags().StringVar(&r.output, "output", "events", "Output format.")
4568 c.Flags().DurationVar(&r.timeout, "timeout", 0,
4669 "How long to wait before exiting")
70+ c.Flags().StringVar(&r.invType, "inv-type", Local, "Type of the inventory info, must be local or remote")
71+ c.Flags().StringVar(&r.inventoryNames, "inv-names", "", "Names of targeted inventory: inv1,inv2,...")
72+ c.Flags().StringVar(&r.namespaces, "namespaces", "", "Names of targeted namespaces: ns1,ns2,...")
73+ c.Flags().StringVar(&r.statuses, "statuses", "", "Targeted status: st1,st2...")
4774
4875 r.Command = c
4976 return r
5077}
5178
52- func Command(f cmdutil.Factory, invFactory inventory.ClientFactory, loader manifestreader.ManifestLoader) *cobra.Command {
53- return GetRunner(f, invFactory, loader).Command
79+ func Command(ctx context.Context, f cmdutil.Factory,
80+ invFactory inventory.ClientFactory, loader Loader) *cobra.Command {
81+ return GetRunner(ctx, f, invFactory, loader).Command
5482}
5583
5684// Runner captures the parameters for the command and contains
5785// the run function.
5886type Runner struct {
87+ ctx context.Context
5988 Command *cobra.Command
6089 factory cmdutil.Factory
6190 invFactory inventory.ClientFactory
@@ -66,49 +95,165 @@ type Runner struct {
6695 timeout time.Duration
6796 output string
6897
69- pollerFactoryFunc func(cmdutil.Factory) (poller.Poller, error)
98+ invType string
99+ inventoryNames string
100+ inventoryNameSet map[string]bool
101+ namespaces string
102+ namespaceSet map[string]bool
103+ statuses string
104+ statusSet map[string]bool
105+
106+ PollerFactoryFunc func(cmdutil.Factory) (poller.Poller, error)
70107}
71108
72- // runE implements the logic of the command and will delegate to the
73- // poller to compute status for each of the resources. One of the printer
74- // implementations takes care of printing the output.
75- func (r *Runner) runE(cmd *cobra.Command, args []string) error {
109+ func (r *Runner) preRunE(*cobra.Command, []string) error {
110+ if !slice.ContainsString(PollUntilOptions, r.pollUntil, nil) {
111+ return fmt.Errorf("pollUntil must be one of %s", strings.Join(PollUntilOptions, ","))
112+ }
113+
114+ if found := pkgprinters.ValidatePrinterType(r.output); !found {
115+ return fmt.Errorf("unknown output type %q", r.output)
116+ }
117+
118+ if r.invType != Local && r.invType != Remote {
119+ return fmt.Errorf("inv-type flag should be either local or remote")
120+ }
121+
122+ if r.invType == Local && r.inventoryNames != "" {
123+ return fmt.Errorf("inv-names flag should only be used when inv-type is set to remote")
124+ }
125+
126+ if r.inventoryNames != "" {
127+ r.inventoryNameSet = make(map[string]bool)
128+ for _, name := range strings.Split(r.inventoryNames, ",") {
129+ r.inventoryNameSet[name] = true
130+ }
131+ }
132+
133+ if r.namespaces != "" {
134+ r.namespaceSet = make(map[string]bool)
135+ for _, ns := range strings.Split(r.namespaces, ",") {
136+ r.namespaceSet[ns] = true
137+ }
138+ }
139+
140+ if r.statuses != "" {
141+ r.statusSet = make(map[string]bool)
142+ for _, st := range strings.Split(r.statuses, ",") {
143+ parsedST := strings.ToLower(st)
144+ r.statusSet[parsedST] = true
145+ }
146+ }
147+
148+ return nil
149+ }
150+
151+ // Load inventory info from local storage
152+ // and get info from the cluster based on the local info
153+ // wrap it to be a map mapping from string to objectMetadataSet
154+ func (r *Runner) loadInvFromDisk(cmd *cobra.Command, args []string) (*printer.PrintData, error) {
76155 inv, err := r.loader.GetInvInfo(cmd, args)
77156 if err != nil {
78- return err
157+ return nil, err
79158 }
80159
81160 invClient, err := r.invFactory.NewClient(r.factory)
82161 if err != nil {
83- return err
162+ return nil, err
84163 }
85164
86165 // Based on the inventory template manifest we look up the inventory
87166 // from the live state using the inventory client.
88167 identifiers, err := invClient.GetClusterObjs(inv)
89168 if err != nil {
90- return err
169+ return nil, err
91170 }
92171
93- // Exit here if the inventory is empty.
94- if len(identifiers) == 0 {
95- _, _ = fmt.Fprint(cmd.OutOrStdout(), "no resources found in the inventory\n")
96- return nil
172+ printData := printer.PrintData{
173+ Identifiers: object.ObjMetadataSet{},
174+ InvNameMap: make(map[object.ObjMetadata]string),
175+ StatusSet: r.statusSet,
176+ }
177+
178+ for _, obj := range identifiers {
179+ // check if the object is under one of the targeted namespaces
180+ if _, ok := r.namespaceSet[obj.Namespace]; ok || len(r.namespaceSet) == 0 {
181+ // add to the map for future reference
182+ printData.InvNameMap[obj] = inv.Name()
183+ // append to identifiers
184+ printData.Identifiers = append(printData.Identifiers, obj)
185+ }
186+ }
187+ return &printData, nil
188+ }
189+
190+ // Retrieve a list of inventory object from the cluster
191+ func (r *Runner) listInvFromCluster() (*printer.PrintData, error) {
192+ invClient, err := r.invFactory.NewClient(r.factory)
193+ if err != nil {
194+ return nil, err
97195 }
98196
99197 // initialize maps in printData
100198 printData := printer.PrintData{
101- InvNameMap: make(map[object.ObjMetadata]string),
102- StatusSet: make(map[string]bool),
199+ Identifiers: object.ObjMetadataSet{},
200+ InvNameMap: make(map[object.ObjMetadata]string),
201+ StatusSet: r.statusSet,
103202 }
104- for _, obj := range identifiers {
105- // add to the map for future reference
106- printData.InvNameMap[obj] = inv.Name()
107- // append to identifiers
108- printData.Identifiers = append(printData.Identifiers, obj)
203+
204+ identifiersMap, err := invClient.ListClusterInventoryObjs(r.ctx)
205+ if err != nil {
206+ return nil, err
207+ }
208+
209+ for invName, identifiers := range identifiersMap {
210+ // Check if there are targeted inventory names and include the current inventory name
211+ if _, ok := r.inventoryNameSet[invName]; !ok && len(r.inventoryNameSet) != 0 {
212+ continue
213+ }
214+ // Filter objects
215+ for _, obj := range identifiers {
216+ // check if the object is under one of the targeted namespaces
217+ if _, ok := r.namespaceSet[obj.Namespace]; ok || len(r.namespaceSet) == 0 {
218+ // add to the map for future reference
219+ printData.InvNameMap[obj] = invName
220+ // append to identifiers
221+ printData.Identifiers = append(printData.Identifiers, obj)
222+ }
223+ }
224+ }
225+ return &printData, nil
226+ }
227+
228+ // runE implements the logic of the command and will delegate to the
229+ // poller to compute status for each of the resources. One of the printer
230+ // implementations takes care of printing the output.
231+ func (r *Runner) runE(cmd *cobra.Command, args []string) error {
232+ var printData *printer.PrintData
233+ var err error
234+ switch r.invType {
235+ case Local:
236+ if len(args) != 0 {
237+ printcommon.SprintfWithColor(printcommon.YELLOW,
238+ "Warning: Path is assigned while list flag is enabled, ignore the path")
239+ }
240+ printData, err = r.loadInvFromDisk(cmd, args)
241+ case Remote:
242+ printData, err = r.listInvFromCluster()
243+ default:
244+ return fmt.Errorf("invType must be either local or remote")
245+ }
246+ if err != nil {
247+ return err
248+ }
249+
250+ // Exit here if the inventory is empty.
251+ if len(printData.Identifiers) == 0 {
252+ _, _ = fmt.Fprint(cmd.OutOrStdout(), "no resources found in the inventory\n")
253+ return nil
109254 }
110255
111- statusPoller, err := r.pollerFactoryFunc (r.factory)
256+ statusPoller, err := r.PollerFactoryFunc (r.factory)
112257 if err != nil {
113258 return err
114259 }
@@ -119,7 +264,7 @@ func (r *Runner) runE(cmd *cobra.Command, args []string) error {
119264 In: cmd.InOrStdin(),
120265 Out: cmd.OutOrStdout(),
121266 ErrOut: cmd.ErrOrStderr(),
122- }, & printData)
267+ }, printData)
123268 if err != nil {
124269 return fmt.Errorf("error creating printer: %w", err)
125270 }
@@ -151,11 +296,11 @@ func (r *Runner) runE(cmd *cobra.Command, args []string) error {
151296 return fmt.Errorf("unknown value for pollUntil: %q", r.pollUntil)
152297 }
153298
154- eventChannel := statusPoller.Poll(ctx, identifiers , polling.PollOptions{
299+ eventChannel := statusPoller.Poll(ctx, printData.Identifiers , polling.PollOptions{
155300 PollInterval: r.period,
156301 })
157302
158- return printer.Print(eventChannel, identifiers , cancelFunc)
303+ return printer.Print(eventChannel, printData.Identifiers , cancelFunc)
159304}
160305
161306// desiredStatusNotifierFunc returns an Observer function for the
0 commit comments