From 18b614bb384b1d413554867b8601feb753715575 Mon Sep 17 00:00:00 2001 From: magodo Date: Thu, 21 Jul 2022 09:57:28 +0800 Subject: [PATCH] Batch mode UI improvement (#177) This PR makes several UI improvements: - A tiny improvement on the output dir non-empty warning message, also ensure it is not shown in the `res` mode, as it is meant to be run non-interactively - Removes the warning about missing mapping file when run in `rg` batch mode, which looks too verbose - In `rg` batch mode and `res` mode, rather than printing the status line by line, use a spinner to show the current status, and optionally show some detail info. --- go.mod | 3 +- go.sum | 6 +- internal/meta/meta.go | 12 ++-- internal/run.go | 132 +++++++++++++++++++++++++----------------- main.go | 9 ++- 5 files changed, 96 insertions(+), 66 deletions(-) diff --git a/go.mod b/go.mod index 0ee7cda..6bec7d8 100644 --- a/go.mod +++ b/go.mod @@ -9,13 +9,14 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 github.com/charmbracelet/bubbles v0.10.4-0.20220412141214-292a1dd7ba97 github.com/charmbracelet/bubbletea v0.20.1-0.20220516164627-a5f28a3a04bb - github.com/charmbracelet/lipgloss v0.4.0 + github.com/charmbracelet/lipgloss v0.5.0 github.com/hashicorp/go-version v1.4.0 github.com/hashicorp/hc-install v0.3.3-0.20220510130859-d615262197ec github.com/hashicorp/hcl/v2 v2.11.1 github.com/hashicorp/terraform-exec v0.16.0 github.com/magodo/armid v0.0.0-20220707115142-d2d9f6fb551b github.com/magodo/aztft v0.1.0 + github.com/magodo/spinner v0.0.0-20220720073946-50f31b2dc5a6 github.com/magodo/textinput v0.0.0-20210913072708-7d24f2b4b0c0 github.com/magodo/tfadd v0.10.0 github.com/mitchellh/go-wordwrap v1.0.0 diff --git a/go.sum b/go.sum index dc241da..591de51 100644 --- a/go.sum +++ b/go.sum @@ -102,8 +102,9 @@ github.com/charmbracelet/bubbletea v0.20.1-0.20220516164627-a5f28a3a04bb h1:bRxI github.com/charmbracelet/bubbletea v0.20.1-0.20220516164627-a5f28a3a04bb/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4= github.com/charmbracelet/harmonica v0.1.0 h1:lFKeSd6OAckQ/CEzPVd2mqj+YMEubQ/3FM2IYY3xNm0= github.com/charmbracelet/harmonica v0.1.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss v0.4.0 h1:768h64EFkGUr8V5yAKV7/Ta0NiVceiPaV+PphaW1K9g= github.com/charmbracelet/lipgloss v0.4.0/go.mod h1:vmdkHvce7UzX6xkyf4cca8WlwdQ5RQr8fzta+xl7BOM= +github.com/charmbracelet/lipgloss v0.5.0 h1:lulQHuVeodSgDez+3rGiuxlPVXSnhth442DATR2/8t8= +github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -249,6 +250,8 @@ github.com/magodo/armid v0.0.0-20220707115142-d2d9f6fb551b h1:MizLjMfDMkcTcgb9ho github.com/magodo/armid v0.0.0-20220707115142-d2d9f6fb551b/go.mod h1:rR8E7zfGMbmfnSQvrkFiWYdhrfTqsVSltelnZB09BwA= github.com/magodo/aztft v0.1.0 h1:QyNrMBt7ARNG1EZ+6hvHCEkjG9F/K2C8ERFg7h1Uqhc= github.com/magodo/aztft v0.1.0/go.mod h1:CQemtiO/6opZSdNTU0URJm/ShvIqT7MTsR2OQhyXwY8= +github.com/magodo/spinner v0.0.0-20220720073946-50f31b2dc5a6 h1:CElHO4hPXC+Eivy8sUC/WrnH3jmQzdF2x0lEXBEYul8= +github.com/magodo/spinner v0.0.0-20220720073946-50f31b2dc5a6/go.mod h1:Cn4fFwFH/Ddw9sjWPeSS72bNaxbM+FRXf7pkGEDReq8= github.com/magodo/textinput v0.0.0-20210913072708-7d24f2b4b0c0 h1:aNtr4iNv/tex2t8W1u3scAoNHEnFlTKhNNHOpYStqbs= github.com/magodo/textinput v0.0.0-20210913072708-7d24f2b4b0c0/go.mod h1:MqYhNP+PC386Bjsx5piZe7T4vDm5QIPv8b1RU0prVnU= github.com/magodo/tfadd v0.10.0 h1:HxYLXWksFXldHdaLcQst9Nvr6xJqatgeyGdeSCgM8bA= @@ -290,6 +293,7 @@ github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIW github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw= +github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 h1:QANkGiGr39l1EESqrE0gZw0/AJNYzIvoGLhIoVYtluI= github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= diff --git a/internal/meta/meta.go b/internal/meta/meta.go index f66528b..ec73b66 100644 --- a/internal/meta/meta.go +++ b/internal/meta/meta.go @@ -84,12 +84,14 @@ func NewMeta(cfg config.CommonConfig) (*Meta, error) { } // Interactive mode - fmt.Printf(`The output directory is not empty. Please choose one of actions below: + fmt.Printf(` +The output directory is not empty. Please choose one of actions below: -[O] To overwrite everything inside the output directory, press "O" -[A] To append (state and config) into the output directory, press "A" -[Other] Press other keys to quit -`) +* To overwrite everything inside the output directory, press "O" +* To append (state and config) into the output directory, press "A" +* Press other keys to quit + +> `) var ans string fmt.Scanf("%s", &ans) switch strings.ToLower(ans) { diff --git a/internal/run.go b/internal/run.go index 36176c8..bcf24d6 100644 --- a/internal/run.go +++ b/internal/run.go @@ -2,50 +2,61 @@ package internal import ( "fmt" + "os" + "strings" "github.com/Azure/aztfy/internal/config" "github.com/Azure/aztfy/internal/meta" "github.com/Azure/aztfy/internal/tfaddr" + "github.com/Azure/aztfy/internal/ui/common" + bspinner "github.com/charmbracelet/bubbles/spinner" + "github.com/magodo/spinner" ) func ResourceImport(cfg config.ResConfig) error { - fmt.Printf("Target Azure Resource: %s\n", cfg.ResourceId) c, err := meta.NewResMeta(cfg) if err != nil { return err } - fmt.Println("Initializing...") - if err := c.Init(); err != nil { - return err - } - fmt.Println("Querying Terraform resource type and id...") - rt, tfid, err := c.QueryResourceTypeAndId() - if err != nil { - return err - } + s := bspinner.NewModel() + s.Spinner = common.Spinner - item := meta.ImportItem{ - ResourceID: tfid, - TFAddr: tfaddr.TFAddr{ - Type: rt, - Name: c.ResourceName, - }, - } + return spinner.Run(s, func(msg spinner.Messager) error { + msg.SetStatus("Initializing...") + if err := c.Init(); err != nil { + return err + } - fmt.Printf("\nResource type: %s\nResource Id: %s\n\n", item.TFAddr.Type, item.ResourceID) - fmt.Println("Importing...") - c.Import(&item) - if err := item.ImportError; err != nil { - return fmt.Errorf("failed to import %s as %s: %v", item.ResourceID, item.TFAddr, err) - } + msg.SetStatus("Querying Terraform resource type and id...") + rt, tfid, err := c.QueryResourceTypeAndId() + if err != nil { + return err + } - fmt.Println("Generating Terraform configurations...") - if err := c.GenerateCfg(meta.ImportList{item}); err != nil { - return fmt.Errorf("generating Terraform configuration: %v", err) - } + item := meta.ImportItem{ + ResourceID: tfid, + TFAddr: tfaddr.TFAddr{ + Type: rt, + Name: c.ResourceName, + }, + } + msg.SetDetail(fmt.Sprintf(`Resource Type: %s +Resource Id : %s`, item.TFAddr.Type, item.ResourceID)) + + msg.SetStatus("Importing...") + c.Import(&item) + if err := item.ImportError; err != nil { + return fmt.Errorf("failed to import %s as %s: %v", item.ResourceID, item.TFAddr, err) + } - return nil + msg.SetStatus("Generating Terraform configurations...") + if err := c.GenerateCfg(meta.ImportList{item}); err != nil { + return fmt.Errorf("generating Terraform configuration: %v", err) + } + + return nil + }) } func BatchImport(cfg config.RgConfig, continueOnError bool) error { @@ -54,38 +65,51 @@ func BatchImport(cfg config.RgConfig, continueOnError bool) error { return err } - fmt.Println("Initializing...") - if err := c.Init(); err != nil { - return err - } + s := bspinner.NewModel() + s.Spinner = common.Spinner - fmt.Println("List resources...") - list, err := c.ListResource() - if err != nil { - return err - } + var warnings []string + err = spinner.Run(s, func(msg spinner.Messager) error { + msg.SetStatus("Initializing...") + if err := c.Init(); err != nil { + return err + } - fmt.Println("Import resources...") - for i := range list { - if list[i].Skip() { - fmt.Printf("[WARN] No mapping information for resource: %s, skip it\n", list[i].ResourceID) - continue + msg.SetStatus("Listing resources...") + list, err := c.ListResource() + if err != nil { + return err } - fmt.Printf("Importing %s as %s\n", list[i].ResourceID, list[i].TFAddr) - c.Import(&list[i]) - if err := list[i].ImportError; err != nil { - msg := fmt.Sprintf("Failed to import %s as %s: %v", list[i].ResourceID, list[i].TFAddr, err) - if !continueOnError { - return fmt.Errorf(msg) + + msg.SetStatus("Importing resources...") + for i := range list { + if list[i].Skip() { + warnings = append(warnings, fmt.Sprintf("No mapping information for resource: %s, skip it", list[i].ResourceID)) + msg.SetDetail(strings.Join(warnings, "\n")) + continue + } + msg.SetStatus(fmt.Sprintf("Importing %s as %s", list[i].ResourceID, list[i].TFAddr)) + c.Import(&list[i]) + if err := list[i].ImportError; err != nil { + msg := fmt.Sprintf("Failed to import %s as %s: %v", list[i].ResourceID, list[i].TFAddr, err) + if !continueOnError { + return fmt.Errorf(msg) + } + warnings = append(warnings, msg) } - fmt.Println("[ERROR] " + msg) } - } - fmt.Println("Generating Terraform configurations...") - if err := c.GenerateCfg(list); err != nil { - return fmt.Errorf("generating Terraform configuration: %v", err) + msg.SetStatus("Generating Terraform configurations...") + if err := c.GenerateCfg(list); err != nil { + return fmt.Errorf("generating Terraform configuration: %v", err) + } + return nil + }) + + // Print out the warnings, if any + if len(warnings) != 0 { + fmt.Fprintln(os.Stderr, "Warnings:\n"+strings.Join(warnings, "\n")) } - return nil + return err } diff --git a/main.go b/main.go index 55bda68..b4c924d 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/json" "fmt" - "github.com/magodo/armid" "io" "log" "os" @@ -13,6 +12,8 @@ import ( "strconv" "strings" + "github.com/magodo/armid" + "github.com/Azure/aztfy/internal" "github.com/Azure/aztfy/internal/config" "github.com/Azure/aztfy/internal/ui" @@ -176,9 +177,6 @@ func main() { if c.NArg() > 1 { return fmt.Errorf("More than one resource groups specified") } - if flagBatchMode && flagMappingFile == "" { - fmt.Println("[WARN]: No resource mapping file specified! Only the recognized resources will be imported.") - } if flagContinue && !flagBatchMode { return fmt.Errorf("`--continue` must be used together with `--batch`") } @@ -307,6 +305,7 @@ func main() { OutputDir: flagOutputDir, Overwrite: flagOverwrite, Append: flagAppend, + BatchMode: true, BackendType: flagBackendType, BackendConfig: flagBackendConfig.Value(), }, @@ -323,7 +322,7 @@ func main() { sort.Sort(cli.FlagsByName(app.Flags)) if err := app.Run(os.Args); err != nil { - fmt.Fprintf(os.Stderr, "Error: %v", err) + fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } }