From 9197388d9dffa546a0fede0b68c733cc54845040 Mon Sep 17 00:00:00 2001 From: Chris Marget Date: Fri, 7 Apr 2023 13:52:48 -0400 Subject: [PATCH] add version support to generated provider documentation --- apstra/compatibility/api_versions.go | 39 +++++++++++ apstra/compatibility/api_versions_test.go | 28 ++++++++ apstra/provider.go | 22 ++++--- apstra/utils/compare_slices.go | 15 +++++ apstra/utils/compare_slices_test.go | 80 +++++++++++++++++++++++ docs/index.md | 11 +++- go.mod | 4 +- go.sum | 4 +- 8 files changed, 188 insertions(+), 15 deletions(-) create mode 100644 apstra/compatibility/api_versions.go create mode 100644 apstra/compatibility/api_versions_test.go create mode 100644 apstra/utils/compare_slices.go create mode 100644 apstra/utils/compare_slices_test.go diff --git a/apstra/compatibility/api_versions.go b/apstra/compatibility/api_versions.go new file mode 100644 index 00000000..50b458b8 --- /dev/null +++ b/apstra/compatibility/api_versions.go @@ -0,0 +1,39 @@ +package compatibility + +import ( + "github.com/Juniper/apstra-go-sdk/apstra" + "strings" +) + +func SupportedApiVersions() []string { + us := []string{ + "4.1.0", + "4.1.1", + "4.1.2", + } + them := apstra.ApstraApiSupportedVersions() + + var result []string + for i := range us { + if them.Includes(us[i]) { + result = append(result, us[i]) + } + } + + return result +} + +func SupportedApiVersionsPretty() string { + supportedVers := SupportedApiVersions() + stop := len(supportedVers) - 1 + + for i := range supportedVers { + if i == stop { + supportedVers[i] = "and " + supportedVers[i] + break + } + supportedVers[i] = supportedVers[i] + "," + } + + return strings.Join(supportedVers, " ") +} diff --git a/apstra/compatibility/api_versions_test.go b/apstra/compatibility/api_versions_test.go new file mode 100644 index 00000000..ce6a8279 --- /dev/null +++ b/apstra/compatibility/api_versions_test.go @@ -0,0 +1,28 @@ +package compatibility + +import ( + "terraform-provider-apstra/apstra/utils" + "testing" +) + +func TestSupportedApiVersions(t *testing.T) { + expected := []string{ + "4.1.0", + "4.1.1", + "4.1.2", + } + + result := SupportedApiVersions() + + if !utils.SlicesMatch(expected, result) { + t.Fatalf("expected %v, got %v", expected, result) + } +} + +func TestSupportedApiVersionsPretty(t *testing.T) { + expected := "4.1.0, 4.1.1, and 4.1.2" + result := SupportedApiVersionsPretty() + if expected != result { + t.Fatalf("expected %q; got %q", expected, result) + } +} diff --git a/apstra/provider.go b/apstra/provider.go index 31b94bd8..a93cc471 100644 --- a/apstra/provider.go +++ b/apstra/provider.go @@ -15,6 +15,7 @@ import ( "net/url" "os" "sync" + "terraform-provider-apstra/apstra/compatibility" "time" ) @@ -60,15 +61,18 @@ func (p *Provider) Metadata(_ context.Context, _ provider.MetadataRequest, resp resp.Version = p.Version + "_" + p.Commit } -func (p *Provider) Schema(_ context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { +func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "url": schema.StringAttribute{ - MarkdownDescription: "URL of the apstra server, e.g. `https://:@apstra.juniper.net:443/`\n" + - "If username or password are omitted from URL string, environment variables `" + envApstraUsername + - "` and `" + envApstraPassword + "` will be used. If `url` is omitted, environment variable " + - envApstraUrl + " will be used. When the username or password are embedded in the URL string, any " + - "special characters must be URL-encoded. For example, `pass^word` would become `pass%5eword`. ", + MarkdownDescription: "URL of the apstra server, e.g. `https://apstra.example.com`\n\n" + + "It is possible to include Apstra API credentials in the URL using [standard syntax]" + + "(https://datatracker.ietf.org/doc/html/rfc1738#section-3.1). Care should be taken to ensure " + + "that these credentials aren't accidentally committed to version control, etc... The preferred " + + "approach is to pass the credentials as environment variables `" + envApstraUsername + "` and `" + + envApstraPassword + "`.\n\nIf `url` is omitted, environment variable `" + envApstraUrl + "` can " + + "be used to in its place.\n\nWhen the username or password are embedded in the URL string, any " + + "special characters must be URL-encoded. For example, `pass^word` would become `pass%5eword`.", Optional: true, }, "tls_validation_disabled": schema.BoolAttribute{ @@ -91,8 +95,10 @@ func (p *Provider) Schema(_ context.Context, req provider.SchemaRequest, resp *p Optional: true, }, "experimental": schema.BoolAttribute{ - MarkdownDescription: "Sets a flag in the underlying Apstra SDK client object which enables " + - "'experimental' features. At this time, the only effect is bypassing version compatibility checks.", + MarkdownDescription: fmt.Sprintf("Sets a flag in the underlying Apstra SDK client object "+ + "which enables *experimental* features. At this time, the only effect is bypassing version "+ + "compatibility checks in the SDK. This provider release is tested with Apstra versions %s", + compatibility.SupportedApiVersionsPretty()), Optional: true, }, }, diff --git a/apstra/utils/compare_slices.go b/apstra/utils/compare_slices.go new file mode 100644 index 00000000..2d197007 --- /dev/null +++ b/apstra/utils/compare_slices.go @@ -0,0 +1,15 @@ +package utils + +func SlicesMatch[A comparable](a, b []A) bool { + if len(a) != len(b) { + return false + } + + for i := range a { + if a[i] != b[i] { + return false + } + } + + return true +} diff --git a/apstra/utils/compare_slices_test.go b/apstra/utils/compare_slices_test.go new file mode 100644 index 00000000..06965928 --- /dev/null +++ b/apstra/utils/compare_slices_test.go @@ -0,0 +1,80 @@ +package utils + +import "testing" + +func TestSlicesMatch(t *testing.T) { + + type intTestCase struct { + a []int + b []int + expected bool + } + + type stringTestCase struct { + a []string + b []string + expected bool + } + + intTestCases := []intTestCase{ + { + a: []int{1, 3, 4}, + b: []int{1, 3, 4}, + expected: true, + }, + { + a: []int{1, 3, 4, 5}, + b: []int{1, 3, 4}, + expected: false, + }, + { + a: []int{1, 3, 4}, + b: []int{1, 3, 4, 5}, + expected: false, + }, + { + a: []int{0, 3, 4}, + b: []int{1, 3, 4}, + expected: false, + }, + { + a: []int{1, 3, 0}, + b: []int{1, 3, 4}, + expected: false, + }, + } + + for i, tc := range intTestCases { + result := SlicesMatch(tc.a, tc.b) + if tc.expected != result { + t.Fatalf("int test case %d: expected %t; got %t", i, tc.expected, result) + } + + } + + stringTestCases := []stringTestCase{ + { + a: []string{"foo", "bar"}, + b: []string{"foo", "bar"}, + expected: true, + }, + { + a: []string{"fOo", "bar"}, + b: []string{"foo", "bar"}, + expected: false, + }, + { + a: []string{"foo", "bar", "baz"}, + b: []string{"foo", "bar"}, + expected: false, + }, + } + + for i, tc := range stringTestCases { + result := SlicesMatch(tc.a, tc.b) + if tc.expected != result { + t.Fatalf("string test case %d: expected %t; got %t", i, tc.expected, result) + } + + } +} diff --git a/docs/index.md b/docs/index.md index 09d30a35..6d8b1e9c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -40,7 +40,12 @@ APSTRA_PASS=password - `blueprint_mutex_disabled` (Boolean) Blueprint mutexes are signals that changes are being made in the staging Blueprint and other automation processes (including other instances of Terraform) should wait before beginning to make changes of their own. Set this attribute 'true' to skip locking the mutex(es) which signal exclusive Blueprint access for all Blueprint changes made in this project. - `blueprint_mutex_message` (String) Blueprint mutexes are signals that changes are being made in the staging Blueprint and other automation processes (including other instances of Terraform) should wait before beginning to make changes of their own. The mutexes embed a human-readable field to reduce confusion in the event a mutex needs to be cleared manually. This attribute overrides the default message in that field: "locked by terraform at $DATE". -- `experimental` (Boolean) Sets a flag in the underlying Apstra SDK client object which enables 'experimental' features. At this time, the only effect is bypassing version compatibility checks. +- `experimental` (Boolean) Sets a flag in the underlying Apstra SDK client object which enables *experimental* features. At this time, the only effect is bypassing version compatibility checks in the SDK. This provider release is tested with Apstra versions 4.1.0, 4.1.1, and 4.1.2 - `tls_validation_disabled` (Boolean) Set 'true' to disable TLS certificate validation. -- `url` (String) URL of the apstra server, e.g. `https://:@apstra.juniper.net:443/` -If username or password are omitted from URL string, environment variables `APSTRA_USER` and `APSTRA_PASS` will be used. If `url` is omitted, environment variable APSTRA_URL will be used. When the username or password are embedded in the URL string, any special characters must be URL-encoded. For example, `pass^word` would become `pass%5eword`. +- `url` (String) URL of the apstra server, e.g. `https://apstra.example.com` + +It is possible to include Apstra API credentials in the URL using [standard syntax](https://datatracker.ietf.org/doc/html/rfc1738#section-3.1). Care should be taken to ensure that these credentials aren't accidentally committed to version control, etc... The preferred approach is to pass the credentials as environment variables `APSTRA_USER` and `APSTRA_PASS`. + +If `url` is omitted, environment variable `APSTRA_URL` can be used to in its place. + +When the username or password are embedded in the URL string, any special characters must be URL-encoded. For example, `pass^word` would become `pass%5eword`. diff --git a/go.mod b/go.mod index cc43efd8..35e4b25d 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module terraform-provider-apstra go 1.19 require ( - github.com/Juniper/apstra-go-sdk v0.0.0-20230405031658-185211b52bf3 + github.com/Juniper/apstra-go-sdk v0.0.0-20230407174653-236333bfa984 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/terraform-plugin-docs v0.13.0 github.com/hashicorp/terraform-plugin-framework v1.2.0 @@ -12,7 +12,7 @@ require ( ) // HHMMSS -//replace github.com/Juniper/apstra-go-sdk => github.com/Juniper/apstra-go-sdk v0.0.0-20230401030855-a1b5255e2cfb +//replace github.com/Juniper/apstra-go-sdk => github.com/Juniper/apstra-go-sdk v0.0.0-20230407163453-95a45cfa135f require ( github.com/Masterminds/goutils v1.1.1 // indirect diff --git a/go.sum b/go.sum index 01df6eb8..f2f6146b 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/Juniper/apstra-go-sdk v0.0.0-20230405031658-185211b52bf3 h1:XFArSMUO9pKhBjtGydEOAxvps1aQVugmTigXcdVLKAs= -github.com/Juniper/apstra-go-sdk v0.0.0-20230405031658-185211b52bf3/go.mod h1:EZtDsV2etSqm1OY3MbVGTCRXlRHnFQb2Lgz67tsdf60= +github.com/Juniper/apstra-go-sdk v0.0.0-20230407174653-236333bfa984 h1:q/giRDXXR8Qk8aTLseMZL+EOu2AbgSRZDXBKxBkrW3M= +github.com/Juniper/apstra-go-sdk v0.0.0-20230407174653-236333bfa984/go.mod h1:EZtDsV2etSqm1OY3MbVGTCRXlRHnFQb2Lgz67tsdf60= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=