Skip to content

Commit c151d81

Browse files
authored
Merge pull request #8 from magodo/retry_on_import_error
Retry on import error
2 parents 2d2f1b2 + 55d73b6 commit c151d81

File tree

7 files changed

+114
-45
lines changed

7 files changed

+114
-45
lines changed

.goreleaser.yml

-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ builds:
88
- linux
99
- windows
1010
- darwin
11-
ldflags:
12-
- '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}'
1311
archives:
1412
- replacements:
1513
darwin: Darwin

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.16
55
require (
66
github.com/Azure/azure-sdk-for-go v56.1.0+incompatible
77
github.com/Azure/go-autorest/autorest v0.11.19
8+
github.com/fatih/color v1.12.0
89
github.com/hashicorp/go-azure-helpers v0.16.5
910
github.com/hashicorp/go-multierror v1.1.1 // indirect
1011
github.com/hashicorp/go-version v1.3.0

go.sum

+7
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
112112
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
113113
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
114114
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
115+
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
116+
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
115117
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
116118
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
117119
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
@@ -264,10 +266,14 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+
264266
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
265267
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
266268
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
269+
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
270+
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
267271
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
268272
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
269273
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
270274
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
275+
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
276+
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
271277
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
272278
github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4=
273279
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
@@ -448,6 +454,7 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
448454
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
449455
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
450456
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
457+
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
451458
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
452459
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
453460
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

internal/meta.go

+75-23
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"path/filepath"
1212
"strings"
1313

14+
"github.com/fatih/color"
1415
"github.com/magodo/aztfy/internal/armtemplate"
1516
"github.com/magodo/aztfy/schema"
1617

@@ -147,9 +148,19 @@ func (meta *Meta) ExportArmTemplate(ctx context.Context) error {
147148
if err := json.Unmarshal(raw, &meta.armTemplate); err != nil {
148149
return fmt.Errorf("unmarshalling the template: %w", err)
149150
}
151+
150152
return nil
151153
}
152154

155+
func (meta *Meta) ListAzureResourceIDs() []string {
156+
var ids []string
157+
for _, res := range meta.armTemplate.Resources {
158+
ids = append(ids, res.ID(meta.subscriptionId, meta.resourceGroup))
159+
}
160+
ids = append(ids, armtemplate.ResourceGroupId.ID(meta.subscriptionId, meta.resourceGroup))
161+
return ids
162+
}
163+
153164
type ImportList []ImportItem
154165

155166
func (l ImportList) NonSkipped() ImportList {
@@ -163,10 +174,42 @@ func (l ImportList) NonSkipped() ImportList {
163174
return out
164175
}
165176

177+
func (l ImportList) ImportErrored() ImportList {
178+
var out ImportList
179+
for _, item := range l {
180+
if item.ImportError == nil {
181+
continue
182+
}
183+
out = append(out, item)
184+
}
185+
return out
186+
}
187+
188+
func (l ImportList) Imported() ImportList {
189+
var out ImportList
190+
for _, item := range l.NonSkipped() {
191+
if item.ImportError != nil {
192+
continue
193+
}
194+
out = append(out, item)
195+
}
196+
return out
197+
}
198+
166199
type ImportItem struct {
167-
ResourceID string
168-
Skip bool
200+
// The azure resource id
201+
ResourceID string
202+
203+
// Wether this azure resource should be skipped from importing
204+
Skip bool
205+
206+
// Whether this azure resource failed to import into terraform (this might due to the TFResourceType doesn't match the resource)
207+
ImportError error
208+
209+
// The terraform resource type
169210
TFResourceType string
211+
212+
// The terraform resource name
170213
TFResourceName string
171214
}
172215

@@ -177,12 +220,7 @@ func (item *ImportItem) TFAddr() string {
177220
return item.TFResourceType + "." + item.TFResourceName
178221
}
179222

180-
func (meta *Meta) ResolveImportList(ctx context.Context) (ImportList, error) {
181-
var ids []string
182-
for _, res := range meta.armTemplate.Resources {
183-
ids = append(ids, res.ID(meta.subscriptionId, meta.resourceGroup))
184-
}
185-
ids = append(ids, armtemplate.ResourceGroupId.ID(meta.subscriptionId, meta.resourceGroup))
223+
func (meta *Meta) ResolveImportList(ids []string, ctx context.Context) (ImportList, error) {
186224

187225
// schema, err := meta.tf.ProvidersSchema(ctx)
188226
// if err != nil {
@@ -196,7 +234,7 @@ func (meta *Meta) ResolveImportList(ctx context.Context) (ImportList, error) {
196234
// userResourceMap is used to track the resource types and resource names that are specified by users.
197235
userResourceMap := map[string]map[string]bool{}
198236
reader := bufio.NewReader(os.Stdin)
199-
fmt.Println(`Please input the Terraform resource type and name for each Azure resource in form of "<resource type>.<resource name>. Press enter with no input will skip importing that resource.`)
237+
color.Cyan("\nPlease input either the Terraform resource type and name in the form of \"<resource type>.<resource name>\", or simply enter to skip\n")
200238
for idx, id := range ids {
201239
item := ImportItem{
202240
ResourceID: id,
@@ -244,7 +282,13 @@ func (meta *Meta) ResolveImportList(ctx context.Context) (ImportList, error) {
244282
return list, nil
245283
}
246284

247-
func (meta *Meta) Import(ctx context.Context, list ImportList) error {
285+
func (meta *Meta) Import(ctx context.Context, list ImportList) (ImportList, error) {
286+
if len(list.NonSkipped()) == 0 {
287+
return nil, nil
288+
}
289+
290+
color.Cyan("\nImport Azure Resources\n")
291+
248292
// Generate a temp Terraform config to include the empty template for each resource.
249293
// This is required for the following importing.
250294
cfgFile := filepath.Join(meta.workspace, "main.tf")
@@ -253,27 +297,39 @@ func (meta *Meta) Import(ctx context.Context, list ImportList) error {
253297
for _, item := range list.NonSkipped() {
254298
tpl, err := meta.tf.Add(ctx, item.TFAddr())
255299
if err != nil {
256-
return fmt.Errorf("generating resource template for %s: %w", item.TFAddr(), err)
300+
return nil, fmt.Errorf("generating resource template for %s: %w", item.TFAddr(), err)
257301
}
258302
tpls = append(tpls, tpl)
259303
}
260304
if err := os.WriteFile(cfgFile, []byte(strings.Join(tpls, "\n")), 0644); err != nil {
261-
return fmt.Errorf("generating resource template cfgFile file: %w", err)
305+
return nil, fmt.Errorf("generating resource template cfgFile file: %w", err)
262306
}
263307
// Remove the temp Terraform config once resources are imported.
264308
// This is due to the fact that "terraform add" will complain the resource to be added already exist in the config, even we are outputting to stdout.
265309
// This should be resolved once hashicorp/terraform#29220 is addressed.
266310
defer os.Remove(cfgFile)
267311

268312
// Import resources
313+
out := ImportList{}
269314
for idx, item := range list.NonSkipped() {
270-
fmt.Printf("[%d/%d] Importing %q as %s\n", idx+1, len(list.NonSkipped()), item.ResourceID, item.TFAddr())
271-
if err := meta.tf.Import(ctx, item.TFAddr(), item.ResourceID); err != nil {
272-
return err
315+
fmt.Printf("[%d/%d] Importing %q as %s...\n", idx+1, len(list.NonSkipped()), item.ResourceID, item.TFAddr())
316+
err := meta.tf.Import(ctx, item.TFAddr(), item.ResourceID)
317+
if err != nil {
318+
emsg := []string{}
319+
for _, el := range strings.Split(err.Error(), "\n") {
320+
el := strings.TrimSpace(el)
321+
if el == "" {
322+
continue
323+
}
324+
emsg = append(emsg, "\t"+el)
325+
}
326+
color.Red(strings.Join(emsg, "\n"))
273327
}
328+
item.ImportError = err
329+
out = append(out, item)
274330
}
275331

276-
return nil
332+
return out, nil
277333
}
278334

279335
type ConfigInfos []ConfigInfo
@@ -290,10 +346,7 @@ func (cfg ConfigInfo) DumpHCL(w io.Writer) (int, error) {
290346
func (meta *Meta) StateToConfig(ctx context.Context, list ImportList) (ConfigInfos, error) {
291347
out := ConfigInfos{}
292348

293-
for _, item := range list.NonSkipped() {
294-
if item.Skip {
295-
continue
296-
}
349+
for _, item := range list.Imported() {
297350
tpl, err := meta.tf.Add(ctx, item.TFAddr(), tfexec.FromState(true))
298351
if err != nil {
299352
return nil, fmt.Errorf("converting terraform state to config for resource %s: %w", item.TFAddr(), err)
@@ -335,7 +388,7 @@ func (meta *Meta) ResolveDependency(ctx context.Context, configs ConfigInfos) (C
335388
out = append(out, cfg)
336389
continue
337390
}
338-
// This should never happen
391+
// This should never happen as we always ensure there is at least one implicit dependency on the resource group for each resource.
339392
if _, ok := depInfo[armId]; !ok {
340393
return nil, fmt.Errorf("can't find resource %q in the arm template", armId.ID(meta.subscriptionId, meta.resourceGroup))
341394
}
@@ -360,7 +413,6 @@ func (meta *Meta) GenerateConfig(cfgs ConfigInfos) error {
360413
return fmt.Errorf("generating main configuration file: %w", err)
361414
}
362415

363-
fmt.Printf("Please find the Terraform state and the config at: %s\n", meta.workspace)
364416
return nil
365417
}
366418

@@ -369,7 +421,7 @@ func (meta *Meta) hclBlockAppendDependency(body *hclwrite.Body, armIds []armtemp
369421
for _, armid := range armIds {
370422
cfg, ok := cfgset[armid]
371423
if !ok {
372-
dependencies = append(dependencies, fmt.Sprintf("# Depending on %q, but it is not imported by Terraform. Please fix it manually.", armid.ID(meta.subscriptionId, meta.resourceGroup)))
424+
dependencies = append(dependencies, fmt.Sprintf("# Depending on %q, which is not imported by Terraform.", armid.ID(meta.subscriptionId, meta.resourceGroup)))
373425
continue
374426
}
375427
dependencies = append(dependencies, cfg.TFAddr()+",")

internal/run.go

+28-7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"context"
55
"io"
66
"log"
7+
8+
"github.com/fatih/color"
79
)
810

911
func Run(ctx context.Context, rg string) error {
@@ -23,16 +25,33 @@ func Run(ctx context.Context, rg string) error {
2325
return err
2426
}
2527

26-
importList, err := meta.ResolveImportList(ctx)
27-
if err != nil {
28-
return err
29-
}
28+
ids := meta.ListAzureResourceIDs()
3029

31-
if err := meta.Import(ctx, importList); err != nil {
32-
return err
30+
// Repeat importing resources here to avoid the user incorrectly maps an azure resource to an incorrect terraform resource
31+
var importedList ImportList
32+
for len(ids) != 0 {
33+
l, err := meta.ResolveImportList(ids, ctx)
34+
if err != nil {
35+
return err
36+
}
37+
38+
l, err = meta.Import(ctx, l)
39+
if err != nil {
40+
return err
41+
}
42+
43+
for _, item := range l.Imported() {
44+
importedList = append(importedList, item)
45+
}
46+
47+
importErroredList := l.ImportErrored()
48+
ids = make([]string, 0, len(importErroredList))
49+
for _, item := range importErroredList {
50+
ids = append(ids, item.ResourceID)
51+
}
3352
}
3453

35-
configs, err := meta.StateToConfig(ctx, importList)
54+
configs, err := meta.StateToConfig(ctx, importedList)
3655
if err != nil {
3756
return err
3857
}
@@ -46,5 +65,7 @@ func Run(ctx context.Context, rg string) error {
4665
return err
4766
}
4867

68+
color.Cyan("\nPlease find the Terraform state and the config at: %s\n", meta.workspace)
69+
4970
return nil
5071
}

main.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ var (
1515
)
1616

1717
func init() {
18-
flagVersion = flag.Bool("v", false, "print version")
18+
flagVersion = flag.Bool("v", false, "Print version")
1919
}
2020

2121
const usage = `aztfy [option] <resource group name>
@@ -29,7 +29,7 @@ func main() {
2929
flag.Parse()
3030

3131
if *flagVersion {
32-
fmt.Println(getVersion())
32+
fmt.Println(version)
3333
os.Exit(0)
3434
}
3535

version.go

+1-11
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
11
package main
22

3-
import "fmt"
4-
5-
var (
6-
// Followings are filled by compiler during goreleaser
7-
version string = "dev"
8-
commit string = "unknown"
9-
)
10-
11-
func getVersion() string {
12-
return fmt.Sprintf("%s (%s)", version, commit)
13-
}
3+
var version string = "v0.0.2"

0 commit comments

Comments
 (0)