@@ -6,11 +6,13 @@ package status
66import (
77 "context"
88 "fmt"
9+ ss "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,166 @@ 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, %s, %s, %s" ,
112+ Known , Current , Deleted , Forever )
113+ }
114+
115+ if found := pkgprinters .ValidatePrinterType (r .output ); ! found {
116+ return fmt .Errorf ("unknown output type %q" , r .output )
117+ }
118+
119+ if r .invType != Local && r .invType != Remote {
120+ return fmt .Errorf ("inv-type flag should be either local or remote" )
121+ }
122+
123+ if r .invType == Local && r .inventoryNames != "" {
124+ return fmt .Errorf ("inv-names flag should only be used when inv-type is set to remote" )
125+ }
126+
127+ if r .inventoryNames != "" {
128+ r .inventoryNameSet = make (map [string ]bool )
129+ for _ , name := range ss .Split (r .inventoryNames , "," ) {
130+ r .inventoryNameSet [name ] = true
131+ }
132+ }
133+
134+ if r .namespaces != "" {
135+ r .namespaceSet = make (map [string ]bool )
136+ for _ , ns := range ss .Split (r .namespaces , "," ) {
137+ r .namespaceSet [ns ] = true
138+ }
139+ }
140+
141+ if r .statuses != "" {
142+ r .statusSet = make (map [string ]bool )
143+ for _ , st := range ss .Split (r .statuses , "," ) {
144+ parsedST := ss .ToLower (st )
145+ r .statusSet [parsedST ] = true
146+ }
147+ }
148+
149+ return nil
150+ }
151+
152+ // Load inventory info from local storage
153+ // and get info from the cluster based on the local info
154+ // wrap it to be a map mapping from string to objectMetadataSet
155+ func (r * Runner ) loadInvFromDisk (cmd * cobra.Command , args []string ) (* printer.PrintData , error ) {
76156 inv , err := r .loader .GetInvInfo (cmd , args )
77157 if err != nil {
78- return err
158+ return nil , err
79159 }
80160
81161 invClient , err := r .invFactory .NewClient (r .factory )
82162 if err != nil {
83- return err
163+ return nil , err
84164 }
85165
86166 // Based on the inventory template manifest we look up the inventory
87167 // from the live state using the inventory client.
88168 identifiers , err := invClient .GetClusterObjs (inv )
89169 if err != nil {
90- return err
170+ return nil , err
91171 }
92172
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
173+ printData := printer.PrintData {
174+ Identifiers : object.ObjMetadataSet {},
175+ InvNameMap : make (map [object.ObjMetadata ]string ),
176+ StatusSet : r .statusSet ,
177+ }
178+
179+ for _ , obj := range identifiers {
180+ // check if the object is under one of the targeted namespaces
181+ if _ , ok := r .namespaceSet [obj .Namespace ]; ok || len (r .namespaceSet ) == 0 {
182+ // add to the map for future reference
183+ printData .InvNameMap [obj ] = inv .Name ()
184+ // append to identifiers
185+ printData .Identifiers = append (printData .Identifiers , obj )
186+ }
187+ }
188+ return & printData , nil
189+ }
190+
191+ // Retrieve a list of inventory object from the cluster
192+ func (r * Runner ) listInvFromCluster () (* printer.PrintData , error ) {
193+ invClient , err := r .invFactory .NewClient (r .factory )
194+ if err != nil {
195+ return nil , err
97196 }
98197
99198 // initialize maps in printData
100199 printData := printer.PrintData {
101- InvNameMap : make (map [object.ObjMetadata ]string ),
102- StatusSet : make (map [string ]bool ),
200+ Identifiers : object.ObjMetadataSet {},
201+ InvNameMap : make (map [object.ObjMetadata ]string ),
202+ StatusSet : r .statusSet ,
103203 }
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 )
204+
205+ identifiersMap , err := invClient .ListClusterInventoryObjs (r .ctx )
206+ if err != nil {
207+ return nil , err
208+ }
209+
210+ for invName , identifiers := range identifiersMap {
211+ // Check if there are targeted inventory names and include the current inventory name
212+ if _ , ok := r .inventoryNameSet [invName ]; ! ok && len (r .inventoryNameSet ) != 0 {
213+ continue
214+ }
215+ // Filter objects
216+ for _ , obj := range identifiers {
217+ // check if the object is under one of the targeted namespaces
218+ if _ , ok := r .namespaceSet [obj .Namespace ]; ok || len (r .namespaceSet ) == 0 {
219+ // add to the map for future reference
220+ printData .InvNameMap [obj ] = invName
221+ // append to identifiers
222+ printData .Identifiers = append (printData .Identifiers , obj )
223+ }
224+ }
225+ }
226+ return & printData , nil
227+ }
228+
229+ // runE implements the logic of the command and will delegate to the
230+ // poller to compute status for each of the resources. One of the printer
231+ // implementations takes care of printing the output.
232+ func (r * Runner ) runE (cmd * cobra.Command , args []string ) error {
233+ var printData * printer.PrintData
234+ var err error
235+ if r .invType == Local {
236+ if len (args ) != 0 {
237+ fmt .Printf ("%c[%dm" , printcommon .ESC , printcommon .YELLOW )
238+ fmt .Println ("Warning: Path is assigned while list flag is enabled, ignore the path" )
239+ fmt .Printf ("%c[%dm" , printcommon .ESC , printcommon .RESET )
240+ }
241+ printData , err = r .loadInvFromDisk (cmd , args )
242+ } else if r .invType == Remote {
243+ printData , err = r .listInvFromCluster ()
244+ } else {
245+ return fmt .Errorf ("invType must be either local or remote" )
246+ }
247+ if err != nil {
248+ return err
249+ }
250+
251+ // Exit here if the inventory is empty.
252+ if len (printData .Identifiers ) == 0 {
253+ _ , _ = fmt .Fprint (cmd .OutOrStdout (), "no resources found in the inventory\n " )
254+ return nil
109255 }
110256
111- statusPoller , err := r .pollerFactoryFunc (r .factory )
257+ statusPoller , err := r .PollerFactoryFunc (r .factory )
112258 if err != nil {
113259 return err
114260 }
@@ -119,7 +265,7 @@ func (r *Runner) runE(cmd *cobra.Command, args []string) error {
119265 In : cmd .InOrStdin (),
120266 Out : cmd .OutOrStdout (),
121267 ErrOut : cmd .ErrOrStderr (),
122- }, & printData )
268+ }, printData )
123269 if err != nil {
124270 return fmt .Errorf ("error creating printer: %w" , err )
125271 }
@@ -151,11 +297,11 @@ func (r *Runner) runE(cmd *cobra.Command, args []string) error {
151297 return fmt .Errorf ("unknown value for pollUntil: %q" , r .pollUntil )
152298 }
153299
154- eventChannel := statusPoller .Poll (ctx , identifiers , polling.PollOptions {
300+ eventChannel := statusPoller .Poll (ctx , printData . Identifiers , polling.PollOptions {
155301 PollInterval : r .period ,
156302 })
157303
158- return printer .Print (eventChannel , identifiers , cancelFunc )
304+ return printer .Print (eventChannel , printData . Identifiers , cancelFunc )
159305}
160306
161307// desiredStatusNotifierFunc returns an Observer function for the
0 commit comments