From 3d4cdcdedd3d98af10737a722ac3fc4c4bb6c732 Mon Sep 17 00:00:00 2001 From: magodo Date: Thu, 27 Jan 2022 16:11:19 +0800 Subject: [PATCH] Skip already imported resources Add a `Imported` flag for each import item, if it is imported then set that flag. In case users hit some import error, the users can adjust the resource types for each import item. In case users adjust the resource type for an already imported resource, the tool will remove that resource from the state and mark it as not imported, which will cause this resource to be imported in the next apply. --- internal/meta/importlist.go | 10 ++++---- internal/meta/meta.go | 2 +- internal/meta/meta_dummy.go | 2 +- internal/meta/meta_impl.go | 11 +++++---- internal/ui/aztfyclient/client.go | 17 +++++++++++--- internal/ui/common/common.go | 1 + internal/ui/importlist/importlist.go | 2 +- internal/ui/importlist/importlist_delegate.go | 10 ++++++++ internal/ui/importlist/item.go | 2 ++ internal/ui/progress/progress.go | 10 ++++---- internal/ui/ui.go | 23 +++---------------- 11 files changed, 51 insertions(+), 39 deletions(-) diff --git a/internal/meta/importlist.go b/internal/meta/importlist.go index 34ef3ab..c5935d7 100644 --- a/internal/meta/importlist.go +++ b/internal/meta/importlist.go @@ -7,6 +7,9 @@ type ImportItem struct { // Whether this azure resource failed to import into terraform (this might due to the TFResourceType doesn't match the resource) ImportError error + // Whether this azure resource has been successfully imported + Imported bool + // Whether this azure resource failed to validate into terraform (tbh, this should reside in UI layer only) ValidateError error @@ -54,11 +57,10 @@ func (l ImportList) ImportErrored() ImportList { func (l ImportList) Imported() ImportList { var out ImportList - for _, item := range l.NonSkipped() { - if item.ImportError != nil { - continue + for _, item := range l { + if item.Imported { + out = append(out, item) } - out = append(out, item) } return out } diff --git a/internal/meta/meta.go b/internal/meta/meta.go index 7effa7f..b6ae06a 100644 --- a/internal/meta/meta.go +++ b/internal/meta/meta.go @@ -7,7 +7,7 @@ type Meta interface { ResourceGroupName() string Workspace() string ListResource() ImportList - CleanTFState() + CleanTFState(addr string) Import(item ImportItem) error GenerateCfg(l ImportList) error } diff --git a/internal/meta/meta_dummy.go b/internal/meta/meta_dummy.go index 916183e..b995270 100644 --- a/internal/meta/meta_dummy.go +++ b/internal/meta/meta_dummy.go @@ -44,7 +44,7 @@ func (m MetaDummy) ListResource() ImportList { } } -func (m MetaDummy) CleanTFState() { +func (m MetaDummy) CleanTFState(_ string) { return } diff --git a/internal/meta/meta_impl.go b/internal/meta/meta_impl.go index 0d9de1a..11f22e4 100644 --- a/internal/meta/meta_impl.go +++ b/internal/meta/meta_impl.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "os" - "path" "path/filepath" "strings" @@ -139,16 +138,18 @@ func (meta MetaImpl) ListResource() ImportList { ids = append(ids, armtemplate.ResourceGroupId.ID(meta.subscriptionId, meta.resourceGroup)) l := make(ImportList, 0, len(ids)) - for _, id := range ids { + for i, id := range ids { l = append(l, ImportItem{ - ResourceID: id, + ResourceID: id, + TFResourceName: fmt.Sprintf("res-%d", i), }) } return l } -func (meta *MetaImpl) CleanTFState() { - os.Remove(path.Join(meta.Workspace(), "terraform.tfstate")) +func (meta *MetaImpl) CleanTFState(addr string) { + ctx := context.TODO() + meta.tf.StateRm(ctx, addr) } func (meta MetaImpl) Import(item ImportItem) error { diff --git a/internal/ui/aztfyclient/client.go b/internal/ui/aztfyclient/client.go index 5b38400..a3be186 100644 --- a/internal/ui/aztfyclient/client.go +++ b/internal/ui/aztfyclient/client.go @@ -37,6 +37,10 @@ type ImportDoneMsg struct { List meta.ImportList } +type CleanTFStateMsg struct { + Addr string +} + type GenerateCfgDoneMsg struct{} type QuitMsg struct{} @@ -75,15 +79,16 @@ func ShowImportError(item meta.ImportItem, idx int, l meta.ImportList) tea.Cmd { func StartImport(c meta.Meta, l meta.ImportList) tea.Cmd { return func() tea.Msg { - c.CleanTFState() return StartImportMsg{List: l} } } func ImportOneItem(c meta.Meta, item meta.ImportItem) tea.Cmd { return func() tea.Msg { - if !item.Skip() { - item.ImportError = c.Import(item) + if !item.Skip() && !item.Imported { + err := c.Import(item) + item.Imported = err == nil + item.ImportError = err } else { // This explicit minor delay is for the sake of a visual effect of the progress bar. time.Sleep(100 * time.Millisecond) @@ -107,6 +112,12 @@ func GenerateCfg(c meta.Meta, l meta.ImportList) tea.Cmd { } } +func CleanTFState(addr string) tea.Cmd { + return func() tea.Msg { + return CleanTFStateMsg{addr} + } +} + func Quit() tea.Cmd { return func() tea.Msg { return QuitMsg{} diff --git a/internal/ui/common/common.go b/internal/ui/common/common.go index 8c36803..4e6b724 100644 --- a/internal/ui/common/common.go +++ b/internal/ui/common/common.go @@ -6,6 +6,7 @@ import ( const ErrorEmoji = "❗️" const WarningEmoji = "❓" +const OKEmoji = "✅" // Colors for dark and light backgrounds. var ( diff --git a/internal/ui/importlist/importlist.go b/internal/ui/importlist/importlist.go index 3bcba15..631584b 100644 --- a/internal/ui/importlist/importlist.go +++ b/internal/ui/importlist/importlist.go @@ -44,7 +44,7 @@ func NewModel(c meta.Meta, l meta.ImportList, idx int) Model { ti := textinput.NewModel() ti.SetCursorMode(textinput.CursorStatic) if !item.Skip() { - ti.SetValue(fmt.Sprintf("%s.%s", item.TFResourceType, item.TFResourceName)) + ti.SetValue(item.TFResourceType) } ti.CandidateWords = candidates items = append(items, Item{ diff --git a/internal/ui/importlist/importlist_delegate.go b/internal/ui/importlist/importlist_delegate.go index d3aefc5..aa936d8 100644 --- a/internal/ui/importlist/importlist_delegate.go +++ b/internal/ui/importlist/importlist_delegate.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "github.com/Azure/aztfy/internal/ui/aztfyclient" "github.com/Azure/aztfy/internal/ui/common" "github.com/Azure/aztfy/schema" @@ -37,6 +38,15 @@ func NewImportItemDelegate() list.ItemDelegate { // Clear the validation error that were set. selItem.v.ValidateError = nil + // Clear the imported flag that were set, which means this resource will be imported again. + // This allows the user to change its mind for importing this resource as another resource type. + // (e.g. vm resource -> either azurerm_virtual_machine or azurerm_linux_virtual_machine) + if selItem.v.Imported { + cmd := aztfyclient.CleanTFState(selItem.v.TFAddr()) + cmds = append(cmds, cmd) + selItem.v.Imported = false + } + // "Enter" focus current selected item setListKeyMapEnabled(m, false) cmd := selItem.textinput.Focus() diff --git a/internal/ui/importlist/item.go b/internal/ui/importlist/item.go index 662afba..2aae764 100644 --- a/internal/ui/importlist/item.go +++ b/internal/ui/importlist/item.go @@ -18,6 +18,8 @@ func (i Item) Title() string { return common.WarningEmoji + i.v.ResourceID case i.v.ImportError != nil: return common.ErrorEmoji + i.v.ResourceID + case i.v.Imported: + return common.OKEmoji + i.v.ResourceID default: return i.v.ResourceID } diff --git a/internal/ui/progress/progress.go b/internal/ui/progress/progress.go index afa9e76..857f17c 100644 --- a/internal/ui/progress/progress.go +++ b/internal/ui/progress/progress.go @@ -109,10 +109,12 @@ func (m Model) View() string { switch { case res.item.Skip(): s += fmt.Sprintf("%s %s skipped\n", res.emoji, res.item.ResourceID) - case res.item.ImportError == nil: - s += fmt.Sprintf("%s %s import successfully\n", res.emoji, res.item.ResourceID) - case res.item.ImportError != nil: - s += fmt.Sprintf("%s %s import failed\n", res.emoji, res.item.ResourceID) + default: + if res.item.ImportError == nil { + s += fmt.Sprintf("%s %s import successfully\n", res.emoji, res.item.ResourceID) + } else { + s += fmt.Sprintf("%s %s import failed\n", res.emoji, res.item.ResourceID) + } } } } diff --git a/internal/ui/ui.go b/internal/ui/ui.go index d447909..f2fa73b 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -144,26 +144,6 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.importerrormsg = msg return m, nil case aztfyclient.StartImportMsg: - // Update the import list to give each import item a resource name, unique among each resource type. - tm := map[string]map[string]bool{} - for i, item := range msg.List { - if item.Skip() { - continue - } - nm, ok := tm[item.TFResourceType] - if !ok { - nm = map[string]bool{} - tm[item.TFResourceType] = nm - } - for idx := 0; ; idx++ { - name := fmt.Sprintf("this-%d", idx) - if _, ok := nm[name]; !ok { - nm[name] = true - msg.List[i].TFResourceName = name - break - } - } - } m.status = statusImporting m.progress = progress.NewModel(m.meta, msg.List) return m, tea.Batch( @@ -182,6 +162,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } m.status = statusGeneratingCfg return m, aztfyclient.GenerateCfg(m.meta, msg.List) + case aztfyclient.CleanTFStateMsg: + m.meta.CleanTFState(msg.Addr) + return m, nil case aztfyclient.GenerateCfgDoneMsg: m.status = statusSummary return m, nil