From 10b22cc253d819cf1e59247f057f412823889190 Mon Sep 17 00:00:00 2001 From: Krzysztof Klimonda Date: Thu, 29 Aug 2024 15:51:47 +0200 Subject: [PATCH] During Update(), load the existing entry to restore private fields. Some parameters can have codegen override set to skip parameter in terraform provider - this can be used for example when rendering panorama templates to skip attributes related to importing - import is done implicitly when creating other resources. Due to that care has to be taken to make sure those private fields are still copied to pango structures for the update, otherwise Update() call will end up removing them. This implementation follows the old provider, by reading existing entries from the server during Update() call in the provider, and only copying data that is known to the provider, leaving everything else intact. --- pkg/translate/terraform_provider/funcs.go | 65 ++++++---- pkg/translate/terraform_provider/template.go | 114 ++++++++++-------- .../terraform_provider_file.go | 6 +- 3 files changed, 112 insertions(+), 73 deletions(-) diff --git a/pkg/translate/terraform_provider/funcs.go b/pkg/translate/terraform_provider/funcs.go index 60e7662a..002c329d 100644 --- a/pkg/translate/terraform_provider/funcs.go +++ b/pkg/translate/terraform_provider/funcs.go @@ -201,9 +201,16 @@ const copyToPangoTmpl = ` {{- $diag := .Name.LowerCamelCase | printf "%s_diags" }} var {{ $result }}_entry *{{ $.Spec.PangoType }}{{ .Name.CamelCase }} if o.{{ .Name.CamelCase }} != nil { - var {{ $diag }} diag.Diagnostics - {{ $result }}_entry, {{ $diag }} = o.{{ .Name.CamelCase }}.CopyToPango(ctx, encrypted) - diags.Append({{ $diag }}...) + if *obj != nil && (*obj).{{ .Name.CamelCase }} != nil { + {{ $result }}_entry = (*obj).{{ .Name.CamelCase }} + } else { + {{ $result }}_entry = new({{ $.Spec.PangoType }}{{ .Name.CamelCase }}) + } + + diags.Append(o.{{ .Name.CamelCase }}.CopyToPango(ctx, &{{ $result }}_entry, encrypted)...) + if diags.HasError() { + return diags + } } {{- end }} @@ -221,17 +228,23 @@ const copyToPangoTmpl = ` { d := o.{{ .Name.CamelCase }}.ElementsAs(ctx, &{{ $tfEntries }}, false) diags.Append(d...) + if diags.HasError() { + return diags + } for _, elt := range {{ $tfEntries }} { - entry, d := elt.CopyToPango(ctx, encrypted) - diags.Append(d...) + var entry *{{ $pangoType }} + diags.Append(elt.CopyToPango(ctx, &entry, encrypted)...) + if diags.HasError() { + return diags + } {{ $pangoEntries }} = append({{ $pangoEntries }}, *entry) } } {{- else }} - {{ $pangoEntries }} := make([]{{ .ItemsType }}, 0) - { - d := o.{{ .Name.CamelCase }}.ElementsAs(ctx, &{{ $pangoEntries }}, false) - diags.Append(d...) + {{ $pangoEntries }} := make([]{{ .ItemsType }}, 0) + diags.Append(o.{{ .Name.CamelCase }}.ElementsAs(ctx, &{{ $pangoEntries }}, false)...) + if diags.HasError() { + return diags } {{- end }} {{- end }} @@ -246,7 +259,7 @@ const copyToPangoTmpl = ` {{- range .Specs }} {{- $spec := . }} -func (o *{{ .TerraformType }}{{ .ModelOrObject }}) CopyToPango(ctx context.Context, encrypted *map[string]types.String) (*{{ .PangoReturnType }}, diag.Diagnostics) { +func (o *{{ .TerraformType }}{{ .ModelOrObject }}) CopyToPango(ctx context.Context, obj **{{ .PangoReturnType }}, encrypted *map[string]types.String) diag.Diagnostics { var diags diag.Diagnostics {{- range .Params }} {{- $terraformType := printf "%s%s" $spec.TerraformType .Name.CamelCase }} @@ -272,32 +285,33 @@ func (o *{{ .TerraformType }}{{ .ModelOrObject }}) CopyToPango(ctx context.Conte {{- end }} {{- end }} - result := &{{ .PangoReturnType }}{ + if (*obj) == nil { + *obj = new({{ .PangoReturnType }}) + } {{- if .HasEntryName }} - Name: o.Name.ValueString(), + (*obj).Name = o.Name.ValueString() {{- end }} {{- range .Params }} {{- if eq .Type "" }} - {{ .Name.CamelCase }}: {{ .Name.LowerCamelCase }}_entry, + (*obj).{{ .Name.CamelCase }} = {{ .Name.LowerCamelCase }}_entry {{- else if eq .Type "list" }} - {{ .Name.CamelCase }}: {{ .Name.LowerCamelCase }}_pango_entries, + (*obj).{{ .Name.CamelCase }} = {{ .Name.LowerCamelCase }}_pango_entries {{- else }} - {{ .Name.CamelCase }}: {{ .Name.LowerCamelCase }}_value, + (*obj).{{ .Name.CamelCase }} = {{ .Name.LowerCamelCase }}_value {{- end }} {{- end }} {{- range .OneOf }} {{- if eq .Type "" }} - {{ .Name.CamelCase }}: {{ .Name.LowerCamelCase }}_entry, + (*obj).{{ .Name.CamelCase }} = {{ .Name.LowerCamelCase }}_entry {{- else if eq .Type "list" }} - {{ .Name.CamelCase }}: {{ .Name.LowerCamelCase }}_pango_entries, + (*obj).{{ .Name.CamelCase }} = {{ .Name.LowerCamelCase }}_pango_entries {{- else }} - {{ .Name.CamelCase }}: {{ .Name.LowerCamelCase }}_value, + (*obj).{{ .Name.CamelCase }} = {{ .Name.LowerCamelCase }}_value {{- end }} {{- end }} - } - return result, diags + return diags } {{- end }} ` @@ -408,9 +422,10 @@ var {{ .Name.LowerCamelCase }}_list types.List if obj.{{ .Name.CamelCase }} != nil { {{ $result }}_object = new({{ $.Spec.TerraformType }}{{ .Name.CamelCase }}Object) - var {{ $diag }} diag.Diagnostics - {{ $diag }} = {{ $result }}_object.CopyFromPango(ctx, obj.{{ .Name.CamelCase }}, encrypted) - diags.Append({{ $diag }}...) + diags.Append({{ $result }}_object.CopyFromPango(ctx, obj.{{ .Name.CamelCase }}, encrypted)...) + if diags.HasError() { + return diags + } } {{- end }} {{- end }} @@ -418,13 +433,13 @@ var {{ .Name.LowerCamelCase }}_list types.List {{- define "terraformCreateEntryAssignment" }} {{- range .Params }} {{- if eq .Type "" }} - {{- template "terraformCreateEntryAssignmentForParam" Map "Spec" $ "Parameter" . }} + {{- template "EntryAssignmentForParam" Map "Spec" $ "Parameter" . }} {{- end }} {{- end }} {{- range .OneOf }} {{- if eq .Type "" }} - {{- template "terraformCreateEntryAssignmentForParam" Map "Spec" $ "Parameter" . }} + {{- template "EntryAssignmentForParam" Map "Spec" $ "Parameter" . }} {{- end }} {{- end }} {{- end }} diff --git a/pkg/translate/terraform_provider/template.go b/pkg/translate/terraform_provider/template.go index 96e1345a..7fa1cba0 100644 --- a/pkg/translate/terraform_provider/template.go +++ b/pkg/translate/terraform_provider/template.go @@ -410,10 +410,8 @@ state.{{ .ListAttribute.CamelCase }}.ElementsAs(ctx, &elements, false) entries := make([]*{{ $resourceSDKStructName }}, len(elements)) idx := 0 for name, elt := range elements { - var list_diags diag.Diagnostics var entry *{{ .resourceSDKName }}.{{ .EntryOrConfig }} - entry, list_diags = elt.CopyToPango(ctx, {{ $ev }}) - resp.Diagnostics.Append(list_diags...) + resp.Diagnostics.Append(elt.CopyToPango(ctx, &entry, {{ $ev }})...) if resp.Diagnostics.HasError() { return } @@ -433,8 +431,7 @@ for _, elt := range created { continue } var object {{ $resourceTFStructName }} - copy_diags := object.CopyFromPango(ctx, elt, {{ $ev }}) - resp.Diagnostics.Append(copy_diags...) + resp.Diagnostics.Append(object.CopyFromPango(ctx, elt, {{ $ev }})...) if resp.Diagnostics.HasError() { return } @@ -490,10 +487,8 @@ var elements []{{ $resourceTFStructName }} state.{{ .ListAttribute.CamelCase }}.ElementsAs(ctx, &elements, false) entries := make([]*{{ $resourceSDKStructName }}, len(elements)) for idx, elt := range elements { - var list_diags diag.Diagnostics var entry *{{ $resourceSDKStructName }} - entry, list_diags = elt.CopyToPango(ctx, {{ $ev }}) - resp.Diagnostics.Append(list_diags...) + resp.Diagnostics.Append(elt.CopyToPango(ctx, &entry, {{ $ev }})...) if resp.Diagnostics.HasError() { return } @@ -614,15 +609,13 @@ const resourceCreateFunction = ` // Load the desired config. var obj *{{ .resourceSDKName }}.{{ .EntryOrConfig }} - {{ $ev := "nil" }} {{- if .HasEncryptedResources }} {{ $ev = "&ev" }} ev := make(map[string]types.String) {{- end }} - obj, diags := state.CopyToPango(ctx, {{ $ev }}) - resp.Diagnostics.Append(diags...) - if diags.HasError() { + resp.Diagnostics.Append(state.CopyToPango(ctx, &obj, {{ $ev }})...) + if resp.Diagnostics.HasError() { return } @@ -730,8 +723,8 @@ if resp.Diagnostics.HasError() { entries := make([]*{{ $resourceSDKStructName }}, 0, len(elements)) for name, elt := range elements { - entry, err := elt.CopyToPango(ctx, {{ $ev }}) - resp.Diagnostics.Append(err...) + var entry *{{ $resourceSDKStructName }} + resp.Diagnostics.Append(elt.CopyToPango(ctx, &entry, {{ $ev }})...) if resp.Diagnostics.HasError() { return } @@ -818,8 +811,8 @@ var elements []{{ $resourceTFStructName }} state.{{ .ListAttribute.CamelCase }}.ElementsAs(ctx, &elements, false) entries := make([]*{{ $resourceSDKStructName }}, 0, len(elements)) for _, elt := range elements { - entry, err := elt.CopyToPango(ctx, {{ $ev }}) - resp.Diagnostics.Append(err...) + var entry *{{ $resourceSDKStructName }} + resp.Diagnostics.Append(elt.CopyToPango(ctx, &entry, {{ $ev }})...) if resp.Diagnostics.HasError() { return } @@ -985,10 +978,8 @@ state.{{ .ListAttribute.CamelCase }}.ElementsAs(ctx, &elements, false) stateEntries := make([]*{{ $resourceSDKStructName }}, len(elements)) idx := 0 for name, elt := range elements { - var list_diags diag.Diagnostics var entry *{{ $resourceSDKStructName }} - entry, list_diags = elt.CopyToPango(ctx, nil) - resp.Diagnostics.Append(list_diags...) + resp.Diagnostics.Append(elt.CopyToPango(ctx, &entry, nil)...) if resp.Diagnostics.HasError() { return } @@ -997,17 +988,27 @@ for name, elt := range elements { idx++ } +existing, err := r.manager.ReadMany(ctx, location, stateEntries) +if err != nil && !sdkerrors.IsObjectNotFound(err) { + resp.Diagnostics.AddError("Error while reading entries from the server", err.Error()) + return +} + +existingEntriesByName := make(map[string]*{{ $resourceSDKStructName }}, len(existing)) +for _, elt := range existing { + existingEntriesByName[elt.Name] = elt +} + plan.{{ .ListAttribute.CamelCase }}.ElementsAs(ctx, &elements, false) planEntries := make([]*{{ $resourceSDKStructName }}, len(elements)) idx = 0 for name, elt := range elements { - var list_diags diag.Diagnostics - var entry *{{ $resourceSDKStructName }} - entry, list_diags = elt.CopyToPango(ctx, nil) - resp.Diagnostics.Append(list_diags...) + entry, _ := existingEntriesByName[name] + resp.Diagnostics.Append(elt.CopyToPango(ctx, &entry, nil)...) if resp.Diagnostics.HasError() { return } + entry.Name = name planEntries[idx] = entry idx++ @@ -1065,37 +1066,45 @@ var elements []{{ $resourceTFStructName }} state.{{ .ListAttribute.CamelCase }}.ElementsAs(ctx, &elements, false) stateEntries := make([]*{{ $resourceSDKStructName }}, len(elements)) for idx, elt := range elements { - var list_diags diag.Diagnostics var entry *{{ $resourceSDKStructName }} - entry, list_diags = elt.CopyToPango(ctx, nil) - resp.Diagnostics.Append(list_diags...) + resp.Diagnostics.Append(elt.CopyToPango(ctx, &entry, nil)...) if resp.Diagnostics.HasError() { return } stateEntries[idx] = entry } +{{ $exhaustive := "sdkmanager.NonExhaustive" }} +{{- if .Exhaustive }} + {{ $exhaustive = "sdkmanager.Exhaustive" }} +trueValue := true +position := rule.Position{First: &trueValue} +{{- else }} +position := state.Position.CopyToPango() +{{- end }} + +existing, err := r.manager.ReadMany(ctx, location, stateEntries, {{ $exhaustive }}) +if err != nil && !sdkerrors.IsObjectNotFound(err) { + resp.Diagnostics.AddError("Error while reading entries from the server", err.Error()) + return +} + +existingEntriesByName := make(map[string]*{{ $resourceSDKStructName }}, len(existing)) +for _, elt := range existing { + existingEntriesByName[elt.Name] = elt +} + plan.{{ .ListAttribute.CamelCase }}.ElementsAs(ctx, &elements, false) planEntries := make([]*{{ $resourceSDKStructName }}, len(elements)) for idx, elt := range elements { - var list_diags diag.Diagnostics - var entry *{{ $resourceSDKStructName }} - entry, list_diags = elt.CopyToPango(ctx, nil) - resp.Diagnostics.Append(list_diags...) + entry, _ := existingEntriesByName[elt.Name.ValueString()] + resp.Diagnostics.Append(elt.CopyToPango(ctx, &entry, nil)...) if resp.Diagnostics.HasError() { return } planEntries[idx] = entry } -{{ $exhaustive := "sdkmanager.NonExhaustive" }} -{{- if .Exhaustive }} - {{ $exhaustive = "sdkmanager.Exhaustive" }} -trueValue := true -position := rule.Position{First: &trueValue} -{{- else }} -position := state.Position.CopyToPango() -{{- end }} processed, err := r.manager.UpdateMany(ctx, location, stateEntries, planEntries, {{ $exhaustive }}, position) if err != nil { resp.Diagnostics.AddError("Failed to udpate entries", err.Error()) @@ -1123,6 +1132,8 @@ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) ` const resourceUpdateFunction = ` +{{ $resourceSDKStructName := printf "%s.%s" .resourceSDKName .EntryOrConfig }} + var plan, state {{ .structName }}Model resp.Diagnostics.Append(req.State.Get(ctx, &state)...) resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) @@ -1159,10 +1170,17 @@ const resourceUpdateFunction = ` return } - // Create the service. +{{- if .HasEntryName }} + obj, err := r.manager.Read(ctx, loc.Location, loc.Name) +{{- else }} + obj, err := r.manager.Read(ctx, loc.Location) +{{- end }} + if err != nil { + resp.Diagnostics.AddError("Error in update", err.Error()) + return + } - obj, copy_diags := plan.CopyToPango(ctx, {{ $ev }}) - resp.Diagnostics.Append(copy_diags...) + resp.Diagnostics.Append(plan.CopyToPango(ctx, &obj, {{ $ev }})...) if resp.Diagnostics.HasError() { return } @@ -1197,7 +1215,7 @@ const resourceUpdateFunction = ` } state.Tfid = types.StringValue(tfid) - copy_diags = state.CopyFromPango(ctx, updated, {{ $ev }}) + copy_diags := state.CopyFromPango(ctx, updated, {{ $ev }}) {{- if .HasEncryptedResources }} ev_map, ev_diags := types.MapValueFrom(ctx, types.StringType, ev) state.EncryptedValues = ev_map @@ -1264,6 +1282,8 @@ if err != nil { ` const resourceDeleteFunction = ` +{{ $resourceSDKStructName := printf "%s.%s" .resourceSDKName .EntryOrConfig }} + var state {{ .structName }}Model resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { @@ -1304,14 +1324,14 @@ const resourceDeleteFunction = ` } {{- else }} +{{- $ev := "nil" }} {{- if .HasEncryptedResources }} + {{- $ev = "&ev" }} ev := make(map[string]types.String) - obj, diags := state.CopyToPango(ctx, &ev) -{{- else }} - obj, diags := state.CopyToPango(ctx, nil) {{- end }} - resp.Diagnostics.Append(diags...) - if diags.HasError() { + var obj *{{ $resourceSDKStructName }} + resp.Diagnostics.Append(state.CopyToPango(ctx, &obj, {{ $ev }})...) + if resp.Diagnostics.HasError() { return } diff --git a/pkg/translate/terraform_provider/terraform_provider_file.go b/pkg/translate/terraform_provider/terraform_provider_file.go index d1ea154b..26dbe282 100644 --- a/pkg/translate/terraform_provider/terraform_provider_file.go +++ b/pkg/translate/terraform_provider/terraform_provider_file.go @@ -188,9 +188,13 @@ func (g *GenerateTerraformProvider) GenerateTerraformResource(resourceTyp proper switch resourceTyp { case properties.ResourceUuid: terraformProvider.ImportManager.AddSdkImport("github.com/PaloAltoNetworks/pango/rule", "") + terraformProvider.ImportManager.AddSdkImport("github.com/PaloAltoNetworks/pango/errors", "sdkerrors") case properties.ResourceEntry: case properties.ResourceUuidPlural: - case properties.ResourceEntryPlural, properties.ResourceCustom: + terraformProvider.ImportManager.AddSdkImport("github.com/PaloAltoNetworks/pango/errors", "sdkerrors") + case properties.ResourceEntryPlural: + terraformProvider.ImportManager.AddSdkImport("github.com/PaloAltoNetworks/pango/errors", "sdkerrors") + case properties.ResourceCustom: } // Generate Resource with entry style