@@ -3,15 +3,25 @@ package main
33import (
44 "context"
55 "fmt"
6+ "net/http"
67 "os"
8+ "os/signal"
79 "strings"
10+ "syscall"
811
912 "github.com/spf13/cobra"
13+ networking "k8s.io/api/networking/v1"
14+ "k8s.io/apimachinery/pkg/runtime"
15+ clientgoscheme "k8s.io/client-go/kubernetes/scheme"
1016 "sigs.k8s.io/aws-load-balancer-controller/pkg/ingress2gateway"
17+ "sigs.k8s.io/aws-load-balancer-controller/pkg/ingress2gateway/console"
1118 "sigs.k8s.io/aws-load-balancer-controller/pkg/ingress2gateway/reader"
1219 "sigs.k8s.io/aws-load-balancer-controller/pkg/ingress2gateway/translate"
1320 "sigs.k8s.io/aws-load-balancer-controller/pkg/ingress2gateway/warnings"
1421 "sigs.k8s.io/aws-load-balancer-controller/pkg/ingress2gateway/writer"
22+ "sigs.k8s.io/controller-runtime/pkg/client"
23+ "sigs.k8s.io/controller-runtime/pkg/client/config"
24+ gwv1 "sigs.k8s.io/gateway-api/apis/v1"
1525)
1626
1727const defaultOutputDir = "./gateway-output"
@@ -25,6 +35,8 @@ func main() {
2535
2636func newRootCommand () * cobra.Command {
2737 opts := & ingress2gateway.MigrateOptions {}
38+ consoleOpts := & ConsoleOptions {Port : 8080 }
39+ var consoleMode bool
2840
2941 cmd := & cobra.Command {
3042 Use : "lbc-migrate" ,
@@ -33,16 +45,27 @@ func newRootCommand() *cobra.Command {
3345resources into Gateway API equivalents (Gateway, HTTPRoute, LoadBalancerConfiguration,
3446TargetGroupConfiguration, ListenerRuleConfiguration etc).
3547
36- Input can come from YAML/JSON files, a directory of manifest files, or a live Kubernetes cluster.` ,
48+ Input can come from YAML/JSON files, a directory of manifest files, or a live Kubernetes cluster.
49+
50+ Use --console to launch a local web UI that compares ingress and gateway dry-run models side by side.` ,
3751 SilenceUsage : true ,
3852 RunE : func (cmd * cobra.Command , args []string ) error {
53+ if consoleMode {
54+ return runConsole (cmd .Context (), consoleOpts )
55+ }
3956 if err := validateFlags (opts ); err != nil {
4057 return err
4158 }
4259 return runMigrate (cmd .Context (), opts )
4360 },
4461 }
4562
63+ // Console flags
64+ cmd .Flags ().BoolVar (& consoleMode , "console" , false ,
65+ "Launch the migration console web UI to compare ingress and gateway dry-run models" )
66+ cmd .Flags ().IntVar (& consoleOpts .Port , "port" , 8080 ,
67+ "Local port for the console web server (only with --console)" )
68+
4669 // Input flags
4770 cmd .Flags ().StringSliceVarP (& opts .Files , "file" , "f" , nil ,
4871 "Comma-separated input YAML/JSON file paths (e.g. -f file1.yaml,file2.yaml)" )
@@ -70,9 +93,10 @@ Input can come from YAML/JSON files, a directory of manifest files, or a live Ku
7093 cmd .Flags ().StringVar (& opts .Split , "split" , ingress2gateway .SplitModeNone ,
7194 "Split output layout. Empty (default) writes one combined file; 'namespace' writes one file per namespace plus a gatewayclass file for cluster-scoped resources" )
7295
73- // Dry-run flag
74- cmd .Flags ().BoolVar (& opts .DryRun , "dry-run" , false ,
75- "Add gateway.k8s.aws/dry-run annotation to generated Gateway manifests so LBC previews the generated AWS resources without creating them" )
96+ // Dry-run flag: default to true so users preview the generated model before
97+ // creating real AWS resources. Pass --dry-run=false to opt out.
98+ cmd .Flags ().BoolVar (& opts .DryRun , "dry-run" , true ,
99+ "Add gateway.k8s.aws/dry-run annotation to generated Gateway manifests so LBC previews the generated AWS resources without creating them. Pass --dry-run=false to generate live Gateway manifests." )
76100
77101 return cmd
78102}
@@ -156,3 +180,49 @@ func runMigrate(ctx context.Context, opts *ingress2gateway.MigrateOptions) error
156180
157181 return ingress2gateway .Migrate (ctx , * opts , readFunc , translate .Translate , writeFunc )
158182}
183+
184+ // ConsoleOptions holds the flags for --console mode.
185+ type ConsoleOptions struct {
186+ Port int
187+ }
188+
189+ func runConsole (ctx context.Context , opts * ConsoleOptions ) error {
190+ scheme := runtime .NewScheme ()
191+ _ = clientgoscheme .AddToScheme (scheme )
192+ _ = gwv1 .Install (scheme )
193+ _ = networking .AddToScheme (scheme )
194+
195+ restConfig , err := config .GetConfig ()
196+ if err != nil {
197+ return fmt .Errorf ("failed to get kubeconfig: %w" , err )
198+ }
199+
200+ k8sClient , err := client .New (restConfig , client.Options {Scheme : scheme })
201+ if err != nil {
202+ return fmt .Errorf ("failed to create kubernetes client: %w" , err )
203+ }
204+
205+ server := console .NewConsoleServer (k8sClient )
206+ addr := fmt .Sprintf ("localhost:%d" , opts .Port )
207+
208+ httpServer := & http.Server {
209+ Addr : addr ,
210+ Handler : server .Handler (),
211+ }
212+
213+ sigCh := make (chan os.Signal , 1 )
214+ signal .Notify (sigCh , syscall .SIGINT , syscall .SIGTERM )
215+
216+ go func () {
217+ <- sigCh
218+ fmt .Fprintln (os .Stderr , "\n Shutting down..." )
219+ httpServer .Shutdown (context .Background ())
220+ }()
221+
222+ fmt .Fprintf (os .Stderr , "Console running at http://%s\n Press Ctrl+C to stop.\n " , addr )
223+
224+ if err := httpServer .ListenAndServe (); err != nil && err != http .ErrServerClosed {
225+ return fmt .Errorf ("failed to start console on %s: %w\n Use --port to specify a different port" , addr , err )
226+ }
227+ return nil
228+ }
0 commit comments