Skip to content

Commit e99c0a9

Browse files
committed
feat(codegen): Use generic custom code dispatcher
1 parent 911b802 commit e99c0a9

File tree

16 files changed

+430
-455
lines changed

16 files changed

+430
-455
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
7+
"github.com/hashicorp/terraform-plugin-framework/types"
8+
)
9+
10+
func (o *ApiKeyResource) OpenCustom(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) {
11+
var data ApiKeyResourceModel
12+
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
13+
if resp.Diagnostics.HasError() {
14+
return
15+
}
16+
17+
username := data.Username.ValueString()
18+
password := data.Password.ValueString()
19+
20+
apiKey, err := o.client.GenerateApiKey(ctx, username, password)
21+
if err != nil {
22+
resp.Diagnostics.AddError("failed to generate API key", err.Error())
23+
return
24+
}
25+
26+
data.ApiKey = types.StringValue(apiKey)
27+
resp.Diagnostics.Append(resp.Result.Set(ctx, &data)...)
28+
}
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"encoding/xml"
6+
"fmt"
7+
8+
sdkerrors "github.com/PaloAltoNetworks/pango/errors"
9+
"github.com/PaloAltoNetworks/pango/util"
10+
"github.com/PaloAltoNetworks/pango/xmlapi"
11+
"github.com/hashicorp/terraform-plugin-framework/datasource"
12+
"github.com/hashicorp/terraform-plugin-framework/resource"
13+
"github.com/hashicorp/terraform-plugin-framework/types"
14+
)
15+
16+
type dgpReq struct {
17+
XMLName xml.Name `xml:"show"`
18+
Cmd string `xml:"dg-hierarchy"`
19+
}
20+
21+
type dgpResp struct {
22+
Result *dgHierarchy `xml:"result>dg-hierarchy"`
23+
}
24+
25+
func (o *dgpResp) results() map[string]string {
26+
ans := make(map[string]string)
27+
28+
if o.Result != nil {
29+
for _, v := range o.Result.Info {
30+
ans[v.Name] = ""
31+
v.results(ans)
32+
}
33+
}
34+
35+
return ans
36+
}
37+
38+
type dgHierarchy struct {
39+
Info []dghInfo `xml:"dg"`
40+
}
41+
42+
type dghInfo struct {
43+
Name string `xml:"name,attr"`
44+
Children []dghInfo `xml:"dg"`
45+
}
46+
47+
func (o *dghInfo) results(ans map[string]string) {
48+
for _, v := range o.Children {
49+
ans[v.Name] = o.Name
50+
v.results(ans)
51+
}
52+
}
53+
54+
type apReq struct {
55+
XMLName xml.Name `xml:"request"`
56+
Info apInfo `xml:"move-dg>entry"`
57+
}
58+
59+
type apInfo struct {
60+
Child string `xml:"name,attr"`
61+
Parent string `xml:"new-parent-dg,omitempty"`
62+
}
63+
64+
func getParents(ctx context.Context, client util.PangoClient, deviceGroup string) (map[string]string, error) {
65+
cmd := &xmlapi.Op{
66+
Command: dgpReq{},
67+
}
68+
69+
var ans dgpResp
70+
if _, _, err := client.Communicate(ctx, cmd, false, &ans); err != nil {
71+
return nil, err
72+
}
73+
74+
return ans.results(), nil
75+
}
76+
77+
func assignParent(ctx context.Context, client util.PangoClient, deviceGroup string, parent string) error {
78+
cmd := &xmlapi.Op{
79+
Command: apReq{
80+
Info: apInfo{
81+
Child: deviceGroup,
82+
Parent: parent,
83+
},
84+
},
85+
}
86+
87+
ans := util.JobResponse{}
88+
if _, _, err := client.Communicate(ctx, cmd, false, &ans); err != nil {
89+
return err
90+
}
91+
if err := client.WaitForJob(ctx, ans.Id, 0, nil); err != nil {
92+
return err
93+
}
94+
95+
return nil
96+
}
97+
98+
func (o *DeviceGroupParentDataSource) ReadCustom(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
99+
100+
var state DeviceGroupParentResourceModel
101+
resp.Diagnostics.Append(req.Config.Get(ctx, &state)...)
102+
if resp.Diagnostics.HasError() {
103+
return
104+
}
105+
106+
name := state.DeviceGroup.ValueString()
107+
hierarchy, err := getParents(ctx, o.client, name)
108+
if err != nil {
109+
if sdkerrors.IsObjectNotFound(err) {
110+
resp.State.RemoveResource(ctx)
111+
} else {
112+
resp.Diagnostics.AddError("Failed to query for the device group parent", err.Error())
113+
}
114+
return
115+
}
116+
117+
parent, ok := hierarchy[name]
118+
if !ok {
119+
resp.Diagnostics.AddError("Failed to query for the device group parent", fmt.Sprintf("Device Group '%s' doesn't exist", name))
120+
return
121+
}
122+
state.Parent = types.StringValue(parent)
123+
124+
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
125+
}
126+
127+
func (o *DeviceGroupParentResource) CreateCustom(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
128+
129+
var state DeviceGroupParentResourceModel
130+
resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...)
131+
if resp.Diagnostics.HasError() {
132+
return
133+
}
134+
135+
deviceGroup := state.DeviceGroup.ValueString()
136+
parent := state.Parent.ValueString()
137+
if err := assignParent(ctx, o.client, deviceGroup, parent); err != nil {
138+
resp.Diagnostics.AddError("Failed to assign parent to the device group", err.Error())
139+
return
140+
}
141+
142+
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
143+
144+
}
145+
func (o *DeviceGroupParentResource) ReadCustom(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
146+
147+
var state DeviceGroupParentResourceModel
148+
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
149+
if resp.Diagnostics.HasError() {
150+
return
151+
}
152+
153+
name := state.DeviceGroup.ValueString()
154+
hierarchy, err := getParents(ctx, o.client, name)
155+
if err != nil {
156+
if sdkerrors.IsObjectNotFound(err) {
157+
resp.State.RemoveResource(ctx)
158+
} else {
159+
resp.Diagnostics.AddError("Failed to query for the device group parent", err.Error())
160+
}
161+
return
162+
}
163+
164+
parent, ok := hierarchy[name]
165+
if !ok {
166+
resp.State.RemoveResource(ctx)
167+
return
168+
}
169+
state.Parent = types.StringValue(parent)
170+
171+
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
172+
173+
}
174+
func (o *DeviceGroupParentResource) UpdateCustom(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
175+
176+
var state DeviceGroupParentResourceModel
177+
resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...)
178+
if resp.Diagnostics.HasError() {
179+
return
180+
}
181+
182+
deviceGroup := state.DeviceGroup.ValueString()
183+
parent := state.Parent.ValueString()
184+
if err := assignParent(ctx, o.client, deviceGroup, parent); err != nil {
185+
resp.Diagnostics.AddError("Failed to assign parent to the device group", err.Error())
186+
return
187+
}
188+
189+
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
190+
191+
}
192+
func (o *DeviceGroupParentResource) DeleteCustom(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
193+
194+
var state DeviceGroupParentResourceModel
195+
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
196+
if resp.Diagnostics.HasError() {
197+
return
198+
}
199+
200+
name := state.DeviceGroup.ValueString()
201+
hierarchy, err := getParents(ctx, o.client, name)
202+
if err != nil {
203+
resp.Diagnostics.AddError("Failed to query for the device group parent", err.Error())
204+
return
205+
}
206+
207+
parent, ok := hierarchy[name]
208+
if !ok {
209+
resp.Diagnostics.AddError("Failed to query for the device group parent", fmt.Sprintf("Device Group '%s' doesn't exist", name))
210+
return
211+
}
212+
213+
if parent != "" {
214+
deviceGroup := state.DeviceGroup.ValueString()
215+
if err := assignParent(ctx, o.client, deviceGroup, ""); err != nil {
216+
resp.Diagnostics.AddError("Failed to assign parent to the device group", err.Error())
217+
return
218+
}
219+
}
220+
221+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"encoding/xml"
6+
"regexp"
7+
8+
"github.com/PaloAltoNetworks/pango/xmlapi"
9+
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
10+
"github.com/hashicorp/terraform-plugin-framework/types"
11+
)
12+
13+
type vmAuthKeyRequest struct {
14+
XMLName xml.Name `xml:"request"`
15+
Lifetime int64 `xml:"bootstrap>vm-auth-key>generate>lifetime"`
16+
}
17+
18+
type vmAuthKeyResponse struct {
19+
XMLName xml.Name `xml:"response"`
20+
Result string `xml:"result"`
21+
}
22+
23+
func (o *VmAuthKeyResource) OpenCustom(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) {
24+
var data VmAuthKeyResourceModel
25+
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
26+
if resp.Diagnostics.HasError() {
27+
return
28+
}
29+
30+
lifetime := data.Lifetime.ValueInt64()
31+
32+
cmd := &xmlapi.Op{
33+
Command: vmAuthKeyRequest{Lifetime: lifetime},
34+
}
35+
36+
var serverResponse vmAuthKeyResponse
37+
if _, _, err := o.client.Communicate(ctx, cmd, false, &serverResponse); err != nil {
38+
resp.Diagnostics.AddError("Failed to generate Authenticaion Key", "Server returned an error: "+err.Error())
39+
return
40+
}
41+
42+
vmAuthKeyRegexp := `VM auth key (?P<vmauthkey>.+) generated. Expires at: (?P<expiration>.+)`
43+
expr := regexp.MustCompile(vmAuthKeyRegexp)
44+
match := expr.FindStringSubmatch(serverResponse.Result)
45+
if match == nil {
46+
resp.Diagnostics.AddError("Failed to parse server response", "Server response did not match regular expression")
47+
return
48+
}
49+
50+
groups := make(map[string]string)
51+
for i, name := range expr.SubexpNames() {
52+
if i != 0 && name != "" {
53+
groups[name] = match[i]
54+
}
55+
}
56+
57+
if vmAuthKey, found := groups["vmauthkey"]; found {
58+
data.VmAuthKey = types.StringValue(vmAuthKey)
59+
} else {
60+
resp.Diagnostics.AddError("Failed to parse server response", "Server response did not contain matching authentication key")
61+
return
62+
}
63+
64+
if expiration, found := groups["expiration"]; found {
65+
data.ExpirationDate = types.StringValue(expiration)
66+
} else {
67+
resp.Diagnostics.AddWarning("Incomplete server response", "Server response didn't contain a valid expiration date")
68+
}
69+
70+
resp.Diagnostics.Append(resp.Result.Set(ctx, &data)...)
71+
}

pkg/generate/generator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func (c *Creator) RenderTerraformProviderFile(spec *properties.Normalization, ty
8686
return nil, nil, nil, nil, err
8787
}
8888

89-
if err := tfp.GenerateTerraformProviderFile(spec, terraformProvider); err != nil {
89+
if err := tfp.GenerateTerraformProviderFile(typ, spec, terraformProvider); err != nil {
9090
return nil, nil, nil, nil, err
9191
}
9292

pkg/properties/normalized.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ type TerraformProviderConfig struct {
8585
SkipDatasource bool `json:"skip_datasource" yaml:"skip_datasource"`
8686
SkipDatasourceListing bool `json:"skip_datasource_listing" yaml:"skip_datasource_listing"`
8787
ResourceType TerraformResourceType `json:"resource_type" yaml:"resource_type"`
88-
CustomFuncs map[string]string `json:"custom_functions" yaml:"custom_functions"`
88+
CustomFuncs map[string]bool `json:"custom_functions" yaml:"custom_functions"`
8989
ResourceVariants []TerraformResourceVariant `json:"resource_variants" yaml:"resource_variants"`
9090
Suffix string `json:"suffix" yaml:"suffix"`
9191
PluralSuffix string `json:"plural_suffix" yaml:"plural_suffix"`

pkg/schema/object/object.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ type TerraformConfig struct {
4242
SkipDatasource bool `yaml:"skip_datasource"`
4343
SkipdatasourceListing bool `yaml:"skip_datasource_listing"`
4444
ResourceType TerraformResourceType `yaml:"resource_type"`
45-
CustomFunctions map[string]string `yaml:"custom_functions"`
45+
CustomFunctions map[string]bool `yaml:"custom_functions"`
4646
ResourceVariants []TerraformResourceVariant `yaml:"resource_variants"`
4747
Suffix string `yaml:"suffix"`
4848
PluralSuffix string `yaml:"plural_suffix"`

pkg/translate/terraform_provider/api_key_crud.go

Lines changed: 0 additions & 26 deletions
This file was deleted.

0 commit comments

Comments
 (0)