From 6bb043ec233272066ac6e174e1cbc78f37bfe4e8 Mon Sep 17 00:00:00 2001 From: Marcus Weiner Date: Sun, 29 Sep 2024 20:42:34 +0200 Subject: [PATCH] Implement peer group datasource --- go.mod | 3 + go.sum | 6 - internal/provider/peergroup_datasource.go | 113 ++++++++++++++++++ .../provider/peergroup_datasource_test.go | 55 +++++++++ internal/provider/provider.go | 1 + internal/provider/provider_test.go | 18 +++ 6 files changed, 190 insertions(+), 6 deletions(-) create mode 100644 internal/provider/peergroup_datasource.go create mode 100644 internal/provider/peergroup_datasource_test.go diff --git a/go.mod b/go.mod index 22181e7..9e4e203 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/oapi-codegen/oapi-codegen/v2 v2.3.0 github.com/oapi-codegen/runtime v1.1.1 github.com/sethvargo/go-envconfig v1.1.0 + github.com/stretchr/testify v1.9.0 golang.org/x/tools v0.23.0 ) @@ -30,6 +31,7 @@ require ( github.com/bgentry/speakeasy v0.1.0 // indirect github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect github.com/cloudflare/circl v1.3.7 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.16.0 // indirect github.com/getkin/kin-openapi v0.124.0 // indirect github.com/go-openapi/jsonpointer v0.20.2 // indirect @@ -71,6 +73,7 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/oklog/run v1.0.0 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/posener/complete v1.2.3 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/spf13/cast v1.5.0 // indirect diff --git a/go.sum b/go.sum index d626633..9bccd95 100644 --- a/go.sum +++ b/go.sum @@ -244,8 +244,6 @@ golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/ golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 h1:EDuYyU/MkFXllv9QF9819VlI9a4tzGuCbhG0ExK9o1U= golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -253,8 +251,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -291,8 +287,6 @@ golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/provider/peergroup_datasource.go b/internal/provider/peergroup_datasource.go new file mode 100644 index 0000000..affa69b --- /dev/null +++ b/internal/provider/peergroup_datasource.go @@ -0,0 +1,113 @@ +package provider + +import ( + "context" + "fmt" + + "github.com/ffddorf/terraform-provider-netbox-bgp/client" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var _ datasource.DataSource = &SessionDataSource{} + +func NewPeerGroupDataSource() datasource.DataSource { + return &PeerGroupDataSource{} +} + +type PeerGroupDataSource struct { + client *client.Client +} + +type PeerGroupDataSourceModel struct { + ID types.Int64 `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Description types.String `tfsdk:"description"` + Comments types.String `tfsdk:"comments"` + ExportPolicyIDs []types.Int64 `tfsdk:"export_policy_ids"` + ImportPolicyIDs []types.Int64 `tfsdk:"import_policy_ids"` +} + +func (pgm *PeerGroupDataSourceModel) FillFromAPIModel(resp *client.BGPPeerGroup) { + pgm.ID = maybeInt64Value(resp.Id) + pgm.Name = maybeStringValue(&resp.Name) + pgm.Description = maybeStringValue(&resp.Description) + pgm.Comments = maybeStringValue(resp.Comments) + + if resp.ExportPolicies != nil { + for _, id := range *resp.ExportPolicies { + pgm.ExportPolicyIDs = append(pgm.ExportPolicyIDs, types.Int64Value(int64(id))) + } + } + if resp.ImportPolicies != nil && len(*resp.ImportPolicies) > 0 { + for _, id := range *resp.ImportPolicies { + pgm.ImportPolicyIDs = append(pgm.ImportPolicyIDs, types.Int64Value(int64(id))) + } + } +} + +func (pgd *PeerGroupDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_peergroup" +} + +func (pgd *PeerGroupDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + pgd.client = configureDataSourceClient(req, resp) +} + +var peerGroupDataSchema = map[string]schema.Attribute{ + "id": schema.Int64Attribute{ + MarkdownDescription: "ID of the resource in Netbox to use for lookup", + Required: true, + }, + "name": schema.StringAttribute{ + Computed: true, + }, + "description": schema.StringAttribute{ + Computed: true, + }, + "comments": schema.StringAttribute{ + Computed: true, + }, + "import_policy_ids": schema.ListAttribute{ + ElementType: types.Int64Type, + Computed: true, + }, + "export_policy_ids": schema.ListAttribute{ + ElementType: types.Int64Type, + Computed: true, + }, +} + +func (pgd *PeerGroupDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "BGP peer group data source", + Attributes: peerGroupDataSchema, + } +} + +func (pgd *PeerGroupDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data PeerGroupDataSourceModel + + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + httpRes, err := pgd.client.PluginsBgpBgppeergroupRetrieve(ctx, int(data.ID.ValueInt64())) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("failed to retrieve session: %s", err)) + return + } + res, err := client.ParsePluginsBgpBgppeergroupRetrieveResponse(httpRes) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("failed to parse session: %s", err)) + return + } + if res.JSON200 == nil { + resp.Diagnostics.AddError("Client Error", httpError(httpRes, res.Body)) + return + } + + data.FillFromAPIModel(res.JSON200) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/provider/peergroup_datasource_test.go b/internal/provider/peergroup_datasource_test.go new file mode 100644 index 0000000..ca7381b --- /dev/null +++ b/internal/provider/peergroup_datasource_test.go @@ -0,0 +1,55 @@ +package provider + +import ( + "context" + "fmt" + "strconv" + "testing" + + "github.com/ffddorf/terraform-provider-netbox-bgp/client" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/stretchr/testify/require" +) + +func TestAccPeerGroupDataSource(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + c := testClient(t) + comments := "This will be great!" + r, err := c.PluginsBgpBgppeergroupCreate(ctx, client.BGPPeerGroupRequest{ + Name: "Example Peer Group", + Description: "For testing", + Comments: &comments, + }) + require.NoError(t, err) + pg, err := client.ParsePluginsBgpBgppeergroupCreateResponse(r) + require.NoError(t, err) + require.NotNil(t, pg.JSON201, "bad API response: %d", r.StatusCode) + + peerGroupID := *pg.JSON201.Id + t.Cleanup(func() { + _, _ = c.PluginsBgpBgppeergroupDestroy(ctx, peerGroupID) + }) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + ExternalProviders: testExternalProviders, + Steps: []resource.TestStep{ + // Read testing + { + Config: fmt.Sprintf(` + data "netboxbgp_peergroup" "test" { + id = %d + } + `, peerGroupID), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.netboxbgp_peergroup.test", "id", strconv.Itoa(peerGroupID)), + resource.TestCheckResourceAttr("data.netboxbgp_peergroup.test", "name", "Example Peer Group"), + resource.TestCheckResourceAttr("data.netboxbgp_peergroup.test", "description", "For testing"), + ), + }, + }, + }) +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index a0fd59b..5cb514c 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -179,6 +179,7 @@ func (p *NetboxBGPProvider) DataSources(ctx context.Context) []func() datasource return []func() datasource.DataSource{ NewSessionDataSource, NewSessionsDataSource, + NewPeerGroupDataSource, } } diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index 349ee45..f40df1e 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -4,10 +4,13 @@ package provider import ( + "os" "testing" + "github.com/ffddorf/terraform-provider-netbox-bgp/client" "github.com/hashicorp/terraform-plugin-framework/providerserver" "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/stretchr/testify/require" ) // testAccProtoV6ProviderFactories are used to instantiate a provider during @@ -23,3 +26,18 @@ func testAccPreCheck(t *testing.T) { // about the appropriate environment variables being set are common to see in a pre-check // function. } + +func testClient(t *testing.T) *client.Client { + serverURL, ok := os.LookupEnv("NETBOX_SERVER_URL") + require.True(t, ok, "missing NETBOX_SERVER_URL") + apiToken, ok := os.LookupEnv("NETBOX_API_TOKEN") + require.True(t, ok, "missing NETBOX_API_TOKEN") + + opts := []client.ClientOption{ + client.WithRequestEditorFn(apiKeyAuth(apiToken)), // auth + } + client, err := client.NewClient(serverURL, opts...) + require.NoError(t, err) + + return client +}