Skip to content

Commit

Permalink
Merge pull request #706 from Juniper/feat/705-telemetry-service-regis…
Browse files Browse the repository at this point in the history
…try-data-and-resource-provider

add the code and test for the telemetry service registry entry providers
  • Loading branch information
rajagopalans authored Jul 13, 2024
2 parents 50a8351 + d1de6b3 commit 0c4f104
Show file tree
Hide file tree
Showing 20 changed files with 1,447 additions and 5 deletions.
122 changes: 122 additions & 0 deletions apstra/analytics/telemetry_service_registry_entry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package analytics

import (
"context"
"strings"

"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/Juniper/terraform-provider-apstra/apstra/utils"
"github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
dataSourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/diag"
resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
)

type TelemetryServiceRegistryEntry struct {
ServiceName types.String `tfsdk:"service_name"`
ApplicationSchema jsontypes.Normalized `tfsdk:"application_schema"`
StorageSchemaPath types.String `tfsdk:"storage_schema_path"`
Builtin types.Bool `tfsdk:"built_in"`
Description types.String `tfsdk:"description"`
Version types.String `tfsdk:"version"`
}

func (o TelemetryServiceRegistryEntry) DataSourceAttributes() map[string]dataSourceSchema.Attribute {
return map[string]dataSourceSchema.Attribute{
"service_name": dataSourceSchema.StringAttribute{
MarkdownDescription: "Service Name. Used to identify the Service.",
Required: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"application_schema": dataSourceSchema.StringAttribute{
MarkdownDescription: "Application Schema expressed in JSON",
CustomType: jsontypes.NormalizedType{},
Computed: true,
},
"storage_schema_path": dataSourceSchema.StringAttribute{
MarkdownDescription: "Storage Schema Path",
Computed: true,
},
"description": dataSourceSchema.StringAttribute{
MarkdownDescription: "Description",
Computed: true,
},
"version": dataSourceSchema.StringAttribute{
MarkdownDescription: "Version",
Computed: true,
},
"built_in": dataSourceSchema.BoolAttribute{
MarkdownDescription: "True If built in.",
Computed: true,
},
}
}

func (o TelemetryServiceRegistryEntry) ResourceAttributes() map[string]resourceSchema.Attribute {
return map[string]resourceSchema.Attribute{
"service_name": resourceSchema.StringAttribute{
MarkdownDescription: "Service Name. Used to identify the Service.",
Required: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
},
"application_schema": resourceSchema.StringAttribute{
MarkdownDescription: "Application Schema expressed in JSON",
CustomType: jsontypes.NormalizedType{},
Required: true,
},
"storage_schema_path": resourceSchema.StringAttribute{
MarkdownDescription: "Storage Schema Path. Must be one of:\n - " + strings.Join(utils.AllStorageSchemaPaths(), "\n - ") + "\n",
Required: true,
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
stringvalidator.OneOf(utils.AllStorageSchemaPaths()...),
},
},
"description": resourceSchema.StringAttribute{
MarkdownDescription: "Description",
Optional: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"version": resourceSchema.StringAttribute{
MarkdownDescription: "Version",
Computed: true,
},
"built_in": resourceSchema.BoolAttribute{
MarkdownDescription: "Indicates if provided by Apstra",
Computed: true,
},
}
}

func (o *TelemetryServiceRegistryEntry) LoadApiData(ctx context.Context, in *apstra.TelemetryServiceRegistryEntry, diag *diag.Diagnostics) {
o.ServiceName = types.StringValue(in.ServiceName)
o.Version = types.StringValue(in.Version)
o.Description = utils.StringValueOrNull(ctx, in.Description, diag)
o.Builtin = types.BoolValue(in.Builtin)
o.ApplicationSchema = jsontypes.NewNormalizedValue(string(in.ApplicationSchema))
o.StorageSchemaPath = types.StringValue(utils.StringersToFriendlyString(in.StorageSchemaPath))
}

func (o *TelemetryServiceRegistryEntry) Request(_ context.Context, diags *diag.Diagnostics) *apstra.TelemetryServiceRegistryEntry {
var storageSchemaPath apstra.StorageSchemaPath
err := utils.ApiStringerFromFriendlyString(&storageSchemaPath, o.StorageSchemaPath.ValueString())
if err != nil {
diags.AddError("Failed to Parse Storage Schema Path", err.Error())
return nil
}

return &apstra.TelemetryServiceRegistryEntry{
ServiceName: o.ServiceName.ValueString(),
ApplicationSchema: []byte(o.ApplicationSchema.ValueString()),
StorageSchemaPath: storageSchemaPath,
Builtin: o.Builtin.ValueBool(),
Description: o.Description.ValueString(),
Version: o.Version.ValueString(),
}
}
2 changes: 1 addition & 1 deletion apstra/blueprint/pool_allocation.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (o PoolAllocation) ResourceAttributes() map[string]resourceSchema.Attribute
},
},
"role": resourceSchema.StringAttribute{
MarkdownDescription: "Fabric Role (Apstra Resource Group Name) must be one of:\n\n - " +
MarkdownDescription: "Fabric Role (Apstra Resource Group Name) must be one of:\n - " +
strings.Join(utils.AllResourceGroupNameStrings(), "\n - ") + "\n",
Required: true,
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
Expand Down
89 changes: 89 additions & 0 deletions apstra/data_source_telemetry_service_registry_entries.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package tfapstra

import (
"context"

"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)

var (
_ datasource.DataSourceWithConfigure = &dataSourceTelemetryServiceRegistryEntries{}
_ datasourceWithSetClient = &dataSourceTelemetryServiceRegistryEntries{}
)

type dataSourceTelemetryServiceRegistryEntries struct {
client *apstra.Client
}

func (o *dataSourceTelemetryServiceRegistryEntries) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_telemetry_service_registry_entries"
}

func (o *dataSourceTelemetryServiceRegistryEntries) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
configureDataSource(ctx, o, req, resp)
}

func (o *dataSourceTelemetryServiceRegistryEntries) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: docCategoryDesign + "This data source returns the Service Names of Telemetry Service Registry Entries.",
Attributes: map[string]schema.Attribute{
"service_names": schema.SetAttribute{
MarkdownDescription: "A set of Service Names",
Computed: true,
ElementType: types.StringType,
},
"built_in": schema.BoolAttribute{
MarkdownDescription: "Return only built_in if true, not built_in if false, all otherwise",
Optional: true,
},
},
}
}

func (o *dataSourceTelemetryServiceRegistryEntries) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var config struct {
ServiceNames types.Set `tfsdk:"service_names"`
BuiltIn types.Bool `tfsdk:"built_in"`
}

// get the configuration
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}

tses, err := o.client.GetAllTelemetryServiceRegistryEntries(ctx)
if err != nil {
resp.Diagnostics.AddError("error retrieving Telemetry Service Registry Entries", err.Error())
return
}
var snames []string
for _, t := range tses {
if config.BuiltIn.IsUnknown() || config.BuiltIn.IsNull() || config.BuiltIn.ValueBool() == t.Builtin {
snames = append(snames, t.ServiceName)
}
}

snameSet, diags := types.SetValueFrom(ctx, types.StringType, snames)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

// create new state object
var state struct {
ServiceNames types.Set `tfsdk:"service_names"`
BuiltIn types.Bool `tfsdk:"built_in"`
}
state.ServiceNames = snameSet
state.BuiltIn = config.BuiltIn
// set state
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func (o *dataSourceTelemetryServiceRegistryEntries) setClient(client *apstra.Client) {
o.client = client
}
71 changes: 71 additions & 0 deletions apstra/data_source_telemetry_service_registry_entry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package tfapstra

import (
"context"
"fmt"

"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/Juniper/terraform-provider-apstra/apstra/analytics"
"github.com/Juniper/terraform-provider-apstra/apstra/utils"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/path"
)

var (
_ datasource.DataSourceWithConfigure = &dataSourceTelemetryServiceRegistryEntry{}
_ datasourceWithSetClient = &dataSourceTelemetryServiceRegistryEntry{}
)

type dataSourceTelemetryServiceRegistryEntry struct {
client *apstra.Client
}

func (o *dataSourceTelemetryServiceRegistryEntry) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_telemetry_service_registry_entry"
}

func (o *dataSourceTelemetryServiceRegistryEntry) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
configureDataSource(ctx, o, req, resp)
}

func (o *dataSourceTelemetryServiceRegistryEntry) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: docCategoryDesign + "This data source provides details of a specific Telemetry Service Registry Entry.",
Attributes: analytics.TelemetryServiceRegistryEntry{}.DataSourceAttributes(),
}
}

func (o *dataSourceTelemetryServiceRegistryEntry) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var config analytics.TelemetryServiceRegistryEntry
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}

api, err := o.client.GetTelemetryServiceRegistryEntry(ctx, config.ServiceName.ValueString())
if utils.IsApstra404(err) {
resp.Diagnostics.AddAttributeError(
path.Root("name"),
"TelemetryServiceRegistryEntry not found",
fmt.Sprintf("TelemetryServiceRegistryEntry with Name %q not found", config.ServiceName.ValueString()))
return
}
if err != nil { // catch errors other than 404 from above
resp.Diagnostics.AddError("Error retrieving TelemetryServiceRegistryEntry", err.Error())
return
}

// create new state object
var state analytics.TelemetryServiceRegistryEntry
state.LoadApiData(ctx, api, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
}
// Set state
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func (o *dataSourceTelemetryServiceRegistryEntry) setClient(client *apstra.Client) {
o.client = client
}
62 changes: 62 additions & 0 deletions apstra/data_source_telemetry_service_registry_entry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package tfapstra

import (
"context"
"errors"
"fmt"
"strings"
"testing"

testutils "github.com/Juniper/terraform-provider-apstra/apstra/test_utils"
"github.com/Juniper/terraform-provider-apstra/apstra/utils"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)

const (
dataSourceTelemetryServiceRegistryByServiceNameHCL = `
data "apstra_telemetry_service_registry_entry" "test" {
service_name = "%s"
}
`
)

func TestAccDataSourceTelemetryServiceRegistryEntry(t *testing.T) {
ctx := context.Background()

ts := testutils.TelemetryServiceRegistryEntryA(t, ctx)

TestAppSchema := func(state string) error {
var diags diag.Diagnostics
if !utils.JSONEqual(types.StringValue(state), types.StringValue(string(ts.ApplicationSchema)), &diags) {
return fmt.Errorf("input schema does not match output Input %v. Output %v", ts.ApplicationSchema, state)
}
if diags.HasError() {
var sb strings.Builder
for _, d := range diags.Errors() {
sb.WriteString(d.Summary() + "\n")
sb.WriteString(d.Detail() + "\n")
}
return errors.New(sb.String())
}
return nil
}

resource.Test(t, resource.TestCase{
// PreCheck: setup,
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Read by ID
{
Config: insecureProviderConfigHCL + fmt.Sprintf(dataSourceTelemetryServiceRegistryByServiceNameHCL, ts.ServiceName),
Check: resource.ComposeAggregateTestCheckFunc(
// Verify data source/resource fields match
resource.TestCheckResourceAttr("data.apstra_telemetry_service_registry_entry.test", "service_name", ts.ServiceName),
resource.TestCheckResourceAttr("data.apstra_telemetry_service_registry_entry.test", "storage_schema_path", utils.StringersToFriendlyString(ts.StorageSchemaPath)),
resource.TestCheckResourceAttrWith("data.apstra_telemetry_service_registry_entry.test", "application_schema", TestAppSchema),
),
},
},
})
}
3 changes: 3 additions & 0 deletions apstra/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,8 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
func() datasource.DataSource { return &dataSourceRackType{} },
func() datasource.DataSource { return &dataSourceRackTypes{} },
func() datasource.DataSource { return &dataSourceTag{} },
func() datasource.DataSource { return &dataSourceTelemetryServiceRegistryEntries{} },
func() datasource.DataSource { return &dataSourceTelemetryServiceRegistryEntry{} },
func() datasource.DataSource { return &dataSourceTemplateCollapsed{} },
func() datasource.DataSource { return &dataSourceTemplatePodBased{} },
func() datasource.DataSource { return &dataSourceTemplateRackBased{} },
Expand Down Expand Up @@ -576,6 +578,7 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
func() resource.Resource { return &resourcePropertySet{} },
func() resource.Resource { return &resourceRackType{} },
func() resource.Resource { return &resourceTag{} },
func() resource.Resource { return &resourceTelemetryServiceRegistryEntry{} },
func() resource.Resource { return &resourceTemplateCollapsed{} },
func() resource.Resource { return &resourceTemplatePodBased{} },
func() resource.Resource { return &resourceTemplateRackBased{} },
Expand Down
Loading

0 comments on commit 0c4f104

Please sign in to comment.