diff --git a/internal/provider/datasources/service.go b/internal/provider/datasources/service.go new file mode 100644 index 00000000..14a9afa8 --- /dev/null +++ b/internal/provider/datasources/service.go @@ -0,0 +1,151 @@ +package datasources + +import ( + "context" + + config_api_client "github.com/aruba-uxi/terraform-provider-configuration-api/pkg/config-api-client" + "github.com/aruba-uxi/terraform-provider-configuration/internal/provider/util" + "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 = &serviceTestDataSource{} + _ datasource.DataSourceWithConfigure = &serviceTestDataSource{} +) + +func NewServiceTestDataSource() datasource.DataSource { + return &serviceTestDataSource{} +} + +type serviceTestDataSource struct { + client *config_api_client.APIClient +} + +type serviceTestDataSourceModel struct { + Id types.String `tfsdk:"id"` + Category types.String `tfsdk:"category"` + Name types.String `tfsdk:"name"` + Target types.String `tfsdk:"target"` + Template types.String `tfsdk:"template"` + IsEnabled types.String `tfsdk:"is_enabled"` + Filter struct { + ServiceTestID types.String `tfsdk:"service_test_id"` + } `tfsdk:"filter"` +} + +func (d *serviceTestDataSource) Metadata( + _ context.Context, + req datasource.MetadataRequest, + resp *datasource.MetadataResponse, +) { + resp.TypeName = req.ProviderTypeName + "_service_test" +} + +func (d *serviceTestDataSource) Schema( + _ context.Context, + _ datasource.SchemaRequest, + resp *datasource.SchemaResponse, +) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + }, + "category": schema.StringAttribute{ + Computed: true, + }, + "name": schema.StringAttribute{ + Computed: true, + }, + "target": schema.StringAttribute{ + Computed: true, + }, + "template": schema.StringAttribute{ + Computed: true, + }, + "is_enabled": schema.StringAttribute{ + Computed: true, + }, + "filter": schema.SingleNestedAttribute{ + Required: true, + Attributes: map[string]schema.Attribute{ + "service_test_id": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + } +} + +func (d *serviceTestDataSource) Read( + ctx context.Context, + req datasource.ReadRequest, + resp *datasource.ReadResponse, +) { + var state serviceTestDataSourceModel + + // Read configuration from request + diags := req.Config.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + request := d.client.ConfigurationAPI. + ServiceTestsGet(ctx). + Id(state.Filter.ServiceTestID.ValueString()) + + serviceTestResponse, response, err := util.RetryFor429(request.Execute) + errorPresent, errorDetail := util.RaiseForStatus(response, err) + + errorSummary := util.GenerateErrorSummary("read", "uxi_service_test") + + if errorPresent { + resp.Diagnostics.AddError(errorSummary, errorDetail) + return + } + + if len(serviceTestResponse.Items) != 1 { + resp.Diagnostics.AddError(errorSummary, "Could not find specified data source") + return + } + + serviceTest := serviceTestResponse.Items[0] + + state.Id = types.StringValue(serviceTest.Id) + state.Name = types.StringValue(serviceTest.Name) + + // Set state + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +func (d *serviceTestDataSource) Configure( + _ context.Context, + req datasource.ConfigureRequest, + resp *datasource.ConfigureResponse, +) { + // Add a nil check when handling ProviderData because Terraform + // sets that data after it calls the ConfigureProvider RPC. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*config_api_client.APIClient) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + "Data Source type: ServiceTest. Please report this issue to the provider developers.", + ) + return + } + + d.client = client +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index f5223f5b..7fb74ac7 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -169,27 +169,28 @@ func (p *uxiConfigurationProvider) DataSources(_ context.Context) []func() datas return []func() datasource.DataSource{ datasources.NewAgentDataSource, datasources.NewGroupDataSource, + datasources.NewNetworkGroupAssignmentDataSource, datasources.NewSensorDataSource, + datasources.NewSensorGroupAssignmentDataSource, + datasources.NewServiceTestDataSource, datasources.NewWiredNetworkDataSource, datasources.NewWirelessNetworkDataSource, - datasources.NewSensorGroupAssignmentDataSource, - datasources.NewNetworkGroupAssignmentDataSource, } } // Resources defines the resources implemented in the provider. func (p *uxiConfigurationProvider) Resources(_ context.Context) []func() resource.Resource { return []func() resource.Resource{ + resources.NewAgentGroupAssignmentResource, resources.NewAgentResource, resources.NewGroupResource, + resources.NewNetworkGroupAssignmentResource, + resources.NewSensorGroupAssignmentResource, resources.NewSensorResource, + resources.NewServiceTestGroupAssignmentResource, + resources.NewServiceTestResource, resources.NewWiredNetworkResource, resources.NewWirelessNetworkResource, - resources.NewServiceTestResource, - resources.NewSensorGroupAssignmentResource, - resources.NewAgentGroupAssignmentResource, - resources.NewNetworkGroupAssignmentResource, - resources.NewServiceTestGroupAssignmentResource, } } diff --git a/test/mocked/data-sources/service_test.go b/test/mocked/data-sources/service_test.go new file mode 100644 index 00000000..8af308ba --- /dev/null +++ b/test/mocked/data-sources/service_test.go @@ -0,0 +1,159 @@ +package data_source_test + +import ( + "regexp" + "testing" + + "github.com/aruba-uxi/terraform-provider-configuration/test/mocked/provider" + "github.com/aruba-uxi/terraform-provider-configuration/test/mocked/util" + "github.com/h2non/gock" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/nbio/st" +) + +func TestServiceTestDataSource(t *testing.T) { + defer gock.Off() + mockOAuth := util.MockOAuth() + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: provider.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Test Read + { + PreConfig: func() { + util.MockGetServiceTest( + "uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateServiceTestResponseModel("uid", ""), + }, + ), + 3, + ) + }, + Config: provider.ProviderConfig + ` + data "uxi_service_test" "my_service_test" { + filter = { + service_test_id = "uid" + } + } + `, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr( + "data.uxi_service_test.my_service_test", + "id", + "uid", + ), + ), + }, + }, + }) + + mockOAuth.Mock.Disable() +} + +func TestServiceTestDataSource429Handling(t *testing.T) { + defer gock.Off() + mockOAuth := util.MockOAuth() + var mock429 *gock.Response + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: provider.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + + // Test Read + { + PreConfig: func() { + mock429 = gock.New("https://test.api.capenetworks.com"). + Get("/networking-uxi/v1alpha1/service-tests"). + Reply(429). + SetHeaders(util.RateLimitingHeaders) + util.MockGetServiceTest( + "uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateServiceTestResponseModel("uid", ""), + }, + ), + 3, + ) + }, + Config: provider.ProviderConfig + ` + data "uxi_service_test" "my_service_test" { + filter = { + service_test_id = "uid" + } + } + `, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr( + "data.uxi_service_test.my_service_test", + "id", + "uid", + ), + func(s *terraform.State) error { + st.Assert(t, mock429.Mock.Request().Counter, 0) + return nil + }, + ), + }, + }, + }) + + mockOAuth.Mock.Disable() +} + +func TestServiceTestDataSourceHttpErrorHandling(t *testing.T) { + defer gock.Off() + mockOAuth := util.MockOAuth() + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: provider.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // 5xx error + { + PreConfig: func() { + gock.New("https://test.api.capenetworks.com"). + Get("/networking-uxi/v1alpha1/service-tests"). + Reply(500). + JSON(map[string]interface{}{ + "httpStatusCode": 500, + "errorCode": "HPE_GL_ERROR_INTERNAL_SERVER_ERROR", + "message": "Current request cannot be processed due to unknown issue", + "debugId": "12312-123123-123123-1231212", + }) + }, + Config: provider.ProviderConfig + ` + data "uxi_service_test" "my_service_test" { + filter = { + service_test_id = "uid" + } + } + `, + ExpectError: regexp.MustCompile( + `(?s)Current request cannot be processed due to unknown issue\s*DebugID: 12312-123123-123123-1231212`, + ), + }, + // Not found error + { + PreConfig: func() { + util.MockGetServiceTest( + "uid", + util.GeneratePaginatedResponse([]map[string]interface{}{}), + 1, + ) + }, + Config: provider.ProviderConfig + ` + data "uxi_service_test" "my_service_test" { + filter = { + service_test_id = "uid" + } + } + `, + ExpectError: regexp.MustCompile(`Could not find specified data source`), + }, + }, + }) + + mockOAuth.Mock.Disable() +}