@@ -19,6 +19,7 @@ package cmd
1919import (
2020 "fmt"
2121 "os"
22+ "path/filepath"
2223 "slices"
2324 "strings"
2425
@@ -49,8 +50,11 @@ type PrintRunner struct {
4950 // Defaults to YAML.
5051 outputFormat string
5152
52- // The path to the input yaml config file. Value assigned via --input-file flag
53- inputFile string
53+ // inputFiles contains the paths to YAML manifest files to process. Value assigned via --input-files flag.
54+ inputPaths []string
55+
56+ // recursive enables recursive directory traversal when using --input-dir.
57+ recursive bool
5458
5559 // The namespace used to query Gateway API objects. Value assigned via
5660 // --namespace/-n flag.
@@ -88,7 +92,43 @@ func (pr *PrintRunner) PrintGatewayAPIObjects(cmd *cobra.Command, _ []string) er
8892 return fmt .Errorf ("failed to initialize namespace filter: %w" , err )
8993 }
9094
91- gatewayResources , notificationTablesMap , err := i2gw .ToGatewayAPIResources (cmd .Context (), pr .namespaceFilter , pr .inputFile , pr .providers , pr .getProviderSpecificFlags ())
95+ allFiles := []string {}
96+
97+ for _ , path := range pr .inputPaths {
98+ // Check if path is a directory
99+ if info , err := os .Stat (path ); err == nil && info .IsDir () {
100+ // Discover manifest files in directory
101+ dirFiles , err := discoverManifestFiles (path , pr .recursive )
102+ if err != nil {
103+ return fmt .Errorf ("error reading directory %s: %w" , path , err )
104+ }
105+ allFiles = append (allFiles , dirFiles ... )
106+ } else {
107+ allFiles = append (allFiles , path )
108+ }
109+ }
110+
111+ if len (allFiles ) > 0 {
112+ for _ , inputFile := range allFiles {
113+ fmt .Fprintf (os .Stdout , "=== File: %s ===\n " , inputFile )
114+ gatewayResources , notificationTablesMap , err := i2gw .ToGatewayAPIResources (cmd .Context (), pr .namespaceFilter , inputFile , pr .providers , pr .getProviderSpecificFlags ())
115+
116+ for _ , table := range notificationTablesMap {
117+ fmt .Fprintln (os .Stderr , table )
118+ }
119+
120+ if err != nil {
121+ fmt .Fprintf (os .Stdout , "# Error: %v\n " , err )
122+ } else {
123+ pr .outputResult (gatewayResources )
124+ }
125+
126+ fmt .Fprintf (os .Stdout , "=== End: %s ===\n \n " , inputFile )
127+ }
128+ return nil
129+ }
130+
131+ gatewayResources , notificationTablesMap , err := i2gw .ToGatewayAPIResources (cmd .Context (), pr .namespaceFilter , "" , pr .providers , pr .getProviderSpecificFlags ())
92132 if err != nil {
93133 return err
94134 }
@@ -281,16 +321,18 @@ func (pr *PrintRunner) initializeResourcePrinter() error {
281321// 3. If namespace is specified, it filters resources based on that namespace.
282322// 4. If no namespace is specified and reading from the cluster, it attempts to get the namespace from the cluster; if unsuccessful, initialization fails.
283323func (pr * PrintRunner ) initializeNamespaceFilter () error {
324+ hasFileInput := len (pr .inputPaths ) > 0
325+
284326 // When we should use all namespaces, empty string is used as the filter.
285- if pr .allNamespaces || (pr . inputFile != "" && pr .namespace == "" ) {
327+ if pr .allNamespaces || (hasFileInput && pr .namespace == "" ) {
286328 pr .namespaceFilter = ""
287329 return nil
288330 }
289331
290332 // If namespace flag is not specified, try to use the default namespace from the cluster
291333 if pr .namespace == "" {
292334 ns , err := getNamespaceInCurrentContext ()
293- if err != nil && pr .inputFile == "" {
335+ if err != nil && len ( pr .inputPaths ) == 0 {
294336 // When asked to read from the cluster, but getting the current namespace
295337 // failed for whatever reason - do not process the request.
296338 return err
@@ -326,8 +368,11 @@ func newPrintCommand() *cobra.Command {
326368 cmd .Flags ().StringVarP (& pr .outputFormat , "output" , "o" , "yaml" ,
327369 "Output format. One of: (yaml, json, kyaml)." )
328370
329- cmd .Flags ().StringVar (& pr .inputFile , "input-file" , "" ,
330- `Path to the manifest file. When set, the tool will read ingresses from the file instead of reading from the cluster. Supported files are yaml and json.` )
371+ cmd .Flags ().StringSliceVar (& pr .inputPaths , "input-file" , []string {},
372+ `Path to manifest file(s) or directory(s). When set, the tool will read ingresses from the specified files or all YAML files in directories. Supported files are yaml and json.` )
373+
374+ cmd .Flags ().BoolVar (& pr .recursive , "recursive" , false ,
375+ `When used with --input-file, recursively explore nested directories.` )
331376
332377 cmd .Flags ().StringVarP (& pr .namespace , "namespace" , "n" , "" ,
333378 `If present, the namespace scope for this CLI request.` )
@@ -397,3 +442,33 @@ func PrintUnstructuredAsYaml(obj *unstructured.Unstructured) error {
397442
398443 return nil
399444}
445+
446+ // discoverManifestFiles finds all manifest files in a directory
447+ func discoverManifestFiles (dir string , recursive bool ) ([]string , error ) {
448+ var files []string
449+
450+ err := filepath .Walk (dir , func (path string , info os.FileInfo , err error ) error {
451+ if err != nil {
452+ return err
453+ }
454+
455+ // Skip other directories if not in recursive mode
456+ if info .IsDir () && ! recursive && path != dir {
457+ return filepath .SkipDir
458+ }
459+
460+ if ! info .IsDir () && isManifestFile (path ) {
461+ files = append (files , path )
462+ }
463+
464+ return nil
465+ })
466+
467+ return files , err
468+ }
469+
470+ // isManifestFile checks if a file is a YAML or JSON manifest
471+ func isManifestFile (path string ) bool {
472+ ext := strings .ToLower (filepath .Ext (path ))
473+ return ext == ".yaml" || ext == ".yml" || ext == ".json"
474+ }
0 commit comments