-
Notifications
You must be signed in to change notification settings - Fork 45
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
base: main
Are you sure you want to change the base?
Conversation
thogarty
commented
Jul 31, 2024
- 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" { |
There was a problem hiding this comment.
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" { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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. |
… Create method for Fabric EPT
…plates for doc generation
…ed as optional or computed
a3cf620
to
0f97aa0
Compare
@@ -0,0 +1,268 @@ | |||
package precision_time |
There was a problem hiding this comment.
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.)
package precision_time | |
package precision_time_test |
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 |
There was a problem hiding this comment.
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 }} |
There was a problem hiding this comment.
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)...) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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")) |
There was a problem hiding this comment.
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")) |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
eptAdvanceConfiguration := ept.GetAdvanceConfiguration() | ||
parsedEptAdvanceConfiguration, diags := parseAdvanceConfiguration(ctx, &eptAdvanceConfiguration) | ||
if diags.HasError() { | ||
return diags | ||
} | ||
if !reflect.DeepEqual(parsedEptAdvanceConfiguration, fwtypes.NewListNestedObjectValueOfNull[AdvanceConfigurationModel](ctx)) { | ||
*advanceConfiguration = parsedEptAdvanceConfiguration | ||
} |
There was a problem hiding this comment.
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):
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.