Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add equinix_fabric_precision_time resource and data source #745

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

thogarty
Copy link
Contributor

  • Add precision_time resource/data source to internal package
  • Use terraform-plugin-framework for development
  • Add doc templates for the new resource/data_source

@@ -0,0 +1,3 @@
data "equinix_fabric_precision_time" "time_service" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that you can avoid adding a custom doc template for this datasource by moving this file to examples/data-sources/equinix_fabric_precision_time/data-source.tf

@@ -0,0 +1,17 @@
resource "equinix_fabric_precision_time" "ntp" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's more of a tradeoff for these examples than the data source example, but you could merge these resource examples into a single file, with # comments describing each block if necessary, put the merged file at examples/resources/equinix_fabric_precision_time/resource.tf and remove the custom doc template for the resource as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried out this documentation adjustment you suggested for Data Source and Resource and didn't get the results that were expected. I still feel the templates are necessary based on what Fabric team expects before the schema information, and the examples weren't included following that pattern.

@ctreatma
Copy link
Contributor

ctreatma commented Aug 2, 2024

It looks like this branch will need to be rebased on the latest from main to ensure that all necessary validation workflows run for this PR.

@@ -0,0 +1,268 @@
package precision_time
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We encourage black box testing rather than tying tests to private implementation details. The _test package is a Go idiom that allows you to keep tests in the same directory as the code under test, but force those tests to use the code as a consumer would. (Ultimately your team are the owners of this code and the decision as to whether to use black box testing for each resource is entirely up to you.)

Suggested change
package precision_time
package precision_time_test

Comment on lines +11 to +15
Fabric V4 API compatible data resource that allow user to fetch Equinix Fabric Precision Time Service by Uuid

Additional documentation:
* Getting Started: https://docs.equinix.com/en-us/Content/Edge-Services/EPT/EPT.htm
* API: https://developer.equinix.com/dev-docs/fabric/api-reference/fabric-v4-apis#precision-time
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could instead put this in the Description or MarkdownDescription attribute of the data source schema and reference {{ .Description | trimspace }} in the docs template. This would keep the description in the docs while also making available to other tools.

{{tffile "examples/data-sources/fabric_precision_time/get_by_uuid.tf"}}


{{ .SchemaMarkdown }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've usually seen that as {{ .SchemaMarkdown | trimspace }}, which would I think eliminate some of the empty lines at the end of the generated docs.

}

// Set state to fully populated data
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this where you're running into issues due to fields that are only present in the request and not in the response?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, this is the spot. It's related to advance_configuration only at this point. I'm considering separating ntp and ptp into their own attributes:

advance_configuration_ntp and advance_configuration_ptp.

NTP will be in request only and not in response. So it could be marked optional to allow for it being set early and not set later.

PTP will be in request and response so it could stay as optional/computed.

The nested object structure in terraform-plugin-framework along with it's strict checking of attribute behavior has proven difficult for me. Hoping this uncovers an appropriate path forward that doesn't actually involve modifying the schemas to be so different from the API schema similar to how project_id was handled. It can backfire if the project object gets extended or if advance_configuration gets extended.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also happens during Read and Update. Read when importing a resource, and Update when the response doesn't contain the details it expects. Though I haven't been able to test those as much because the Create has gotten a plan error every time advance_configuration is used. When advance_configuration is not used then we're able to proceed without plan errors based on parsing method putting some zero values there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the data source and resource have the same attributes. Is the data source able to load the advance_configuration details on read? Or does that attribute not work at all for data sources?

Is there some other data in the API response that corresponds to the advance_configuration settings? Or a different API endpoint that can be used fetch the advanced configuration details?

func (c *Config) NewFabricClientForSDK(d *schema.ResourceData) *fabricv4.APIClient {
client := c.newFabricClient()
client := c.newFabricClient(context.WithValue(context.Background(), ctxKey("fabricSDKv2"), "no_oauth_reload"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend waiting to make this change until you can update all of the functions calling NewFabricClientForSDK to pass in the correct context. The background context is probably not the right one to latch on to.

@@ -134,18 +137,37 @@ func (c *Config) NewFabricClientForSDK(d *schema.ResourceData) *fabricv4.APIClie
// Shim for Fabric tests.
// Deprecated: when the acceptance package starts to contain API clients for testing/cleanup this will move with them
func (c *Config) NewFabricClientForTesting() *fabricv4.APIClient {
client := c.newFabricClient()
client := c.newFabricClient(context.WithValue(context.Background(), ctxKey("fabricSDKv2"), "no_oauth_reload"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably fine since it's only used in tests, but this could be an opportunity to define a client in the acceptance package for testing/cleanup instead.

//nolint:staticcheck // We should move to subsystem loggers, but that is a much bigger change
transport := logging.NewTransport("Equinix Fabric (fabricv4)", c.authClient.Transport)
if v := ctx.Value(ctxKey("fabricSDKv2")); v == nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO it'd be better to have two functions, one that takes a context and includes authClient creation, and the old one that didn't take a context.

I'm curious what is the benefit of choosing not to create a new authClient, though?

@thogarty thogarty marked this pull request as draft August 7, 2024 21:51
Comment on lines +167 to +174
eptAdvanceConfiguration := ept.GetAdvanceConfiguration()
parsedEptAdvanceConfiguration, diags := parseAdvanceConfiguration(ctx, &eptAdvanceConfiguration)
if diags.HasError() {
return diags
}
if !reflect.DeepEqual(parsedEptAdvanceConfiguration, fwtypes.NewListNestedObjectValueOfNull[AdvanceConfigurationModel](ctx)) {
*advanceConfiguration = parsedEptAdvanceConfiguration
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid setting the advance_configuration attribute when parsing an API response, you should only set that attribute on the model if it was non-null in the response (suggestion may not be entirely valid code):

Suggested change
eptAdvanceConfiguration := ept.GetAdvanceConfiguration()
parsedEptAdvanceConfiguration, diags := parseAdvanceConfiguration(ctx, &eptAdvanceConfiguration)
if diags.HasError() {
return diags
}
if !reflect.DeepEqual(parsedEptAdvanceConfiguration, fwtypes.NewListNestedObjectValueOfNull[AdvanceConfigurationModel](ctx)) {
*advanceConfiguration = parsedEptAdvanceConfiguration
}
eptAdvanceConfiguration := ept.AdvanceConfiguration
if eptAdvanceConfiguration != nil {
parsedEptAdvanceConfiguration, diags := parseAdvanceConfiguration(ctx, eptAdvanceConfiguration)
if diags.HasError() {
return diags
}
if !reflect.DeepEqual(parsedEptAdvanceConfiguration, fwtypes.NewListNestedObjectValueOfNull[AdvanceConfigurationModel](ctx)) {
*advanceConfiguration = parsedEptAdvanceConfiguration
}
}

If there's no way for this property to be included in an API response, you could also just remove this section of code from the parse code path for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants