From 98437042828e3fd27a170c9394104a745ceda7cc Mon Sep 17 00:00:00 2001 From: Debaditya Ray <65864761+Dray56@users.noreply.github.com> Date: Tue, 5 Nov 2024 07:38:51 +0530 Subject: [PATCH] Added service_route_bindings datasource (#55) --- docs/data-sources/service_route_bindings.md | 63 +++ .../data-source.tf | 7 + .../datasource_service_route_bindings.go | 138 +++++++ .../datasource_service_route_bindings_test.go | 94 +++++ .../datasource_service_route_bindings.yaml | 375 ++++++++++++++++++ ...source_service_route_bindings_invalid.yaml | 127 ++++++ internal/provider/provider.go | 1 + internal/provider/provider_test.go | 1 + .../provider/types_service_route_binding.go | 37 ++ migration-guide/Readme.md | 1 + 10 files changed, 844 insertions(+) create mode 100644 docs/data-sources/service_route_bindings.md create mode 100644 examples/data-sources/cloudfoundry_service_route_bindings/data-source.tf create mode 100644 internal/provider/datasource_service_route_bindings.go create mode 100644 internal/provider/datasource_service_route_bindings_test.go create mode 100644 internal/provider/fixtures/datasource_service_route_bindings.yaml create mode 100644 internal/provider/fixtures/datasource_service_route_bindings_invalid.yaml diff --git a/docs/data-sources/service_route_bindings.md b/docs/data-sources/service_route_bindings.md new file mode 100644 index 0000000..f508179 --- /dev/null +++ b/docs/data-sources/service_route_bindings.md @@ -0,0 +1,63 @@ +--- +page_title: "cloudfoundry_service_route_bindings Data Source - terraform-provider-cloudfoundry" +subcategory: "" +description: |- + Gets information on Service Route Bindings the user has access to. +--- + +# cloudfoundry_service_route_bindings (Data Source) + +Gets information on Service Route Bindings the user has access to. + +## Example Usage + +```terraform +data "cloudfoundry_service_route_bindings" "rbs" { + service_instance = "ab65cad9-73fa-4dd4-9c09-87f89b2e77ec" +} + +output "bindings" { + value = data.cloudfoundry_service_route_bindings.rbs +} +``` + + +## Schema + +### Optional + +- `route` (String) The GUID of the route to filter by +- `service_instance` (String) The GUID of the service instance to filter by + +### Read-Only + +- `route_bindings` (Attributes List) The list of route bindings for the given service instance. (see [below for nested schema](#nestedatt--route_bindings)) + + +### Nested Schema for `route_bindings` + +Optional: + +- `annotations` (Map of String) The annotations associated with Cloud Foundry resources. Add as described [here](https://docs.cloudfoundry.org/adminguide/metadata.html#-view-metadata-for-an-object). +- `labels` (Map of String) The labels associated with Cloud Foundry resources. Add as described [here](https://docs.cloudfoundry.org/adminguide/metadata.html#-view-metadata-for-an-object). + +Read-Only: + +- `created_at` (String) The date and time when the resource was created in [RFC3339](https://www.ietf.org/rfc/rfc3339.txt) format. +- `id` (String) The GUID of the object. +- `last_operation` (Attributes) The details of the last operation performed on the resource (see [below for nested schema](#nestedatt--route_bindings--last_operation)) +- `route` (String) The GUID of the route to be bound +- `route_service_url` (String) The URL for the route service. +- `service_instance` (String) The service instance that the route is bound to +- `updated_at` (String) The date and time when the resource was updated in [RFC3339](https://www.ietf.org/rfc/rfc3339.txt) format. + + +### Nested Schema for `route_bindings.last_operation` + +Read-Only: + +- `created_at` (String) The time at which the last operation was created +- `description` (String) A description of the last operation +- `state` (String) The state of the last operation +- `type` (String) The type of the last operation +- `updated_at` (String) The time at which the last operation was updated \ No newline at end of file diff --git a/examples/data-sources/cloudfoundry_service_route_bindings/data-source.tf b/examples/data-sources/cloudfoundry_service_route_bindings/data-source.tf new file mode 100644 index 0000000..1c07145 --- /dev/null +++ b/examples/data-sources/cloudfoundry_service_route_bindings/data-source.tf @@ -0,0 +1,7 @@ +data "cloudfoundry_service_route_bindings" "rbs" { + service_instance = "ab65cad9-73fa-4dd4-9c09-87f89b2e77ec" +} + +output "bindings" { + value = data.cloudfoundry_service_route_bindings.rbs +} \ No newline at end of file diff --git a/internal/provider/datasource_service_route_bindings.go b/internal/provider/datasource_service_route_bindings.go new file mode 100644 index 0000000..53d9b68 --- /dev/null +++ b/internal/provider/datasource_service_route_bindings.go @@ -0,0 +1,138 @@ +package provider + +import ( + "context" + "fmt" + + cfv3client "github.com/cloudfoundry/go-cfclient/v3/client" + "github.com/cloudfoundry/terraform-provider-cloudfoundry/internal/provider/managers" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" +) + +// Ensure provider defined types fully satisfy framework interfaces. +var ( + _ datasource.DataSource = &ServiceRouteBindingsDataSource{} + _ datasource.DataSourceWithConfigure = &ServiceRouteBindingsDataSource{} +) + +func NewServiceRouteBindingsDataSource() datasource.DataSource { + return &ServiceRouteBindingsDataSource{} +} + +type ServiceRouteBindingsDataSource struct { + cfClient *cfv3client.Client +} + +func (d *ServiceRouteBindingsDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_service_route_bindings" +} + +func (d *ServiceRouteBindingsDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Gets information on Service Route Bindings the user has access to.", + + Attributes: map[string]schema.Attribute{ + "service_instance": schema.StringAttribute{ + MarkdownDescription: "The GUID of the service instance to filter by", + Optional: true, + }, + "route": schema.StringAttribute{ + MarkdownDescription: "The GUID of the route to filter by", + Optional: true, + }, + "route_bindings": schema.ListNestedAttribute{ + MarkdownDescription: "The list of route bindings for the given service instance.", + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "service_instance": schema.StringAttribute{ + MarkdownDescription: "The service instance that the route is bound to", + Computed: true, + }, + "route": schema.StringAttribute{ + MarkdownDescription: "The GUID of the route to be bound", + Computed: true, + }, + "route_service_url": schema.StringAttribute{ + MarkdownDescription: "The URL for the route service.", + Computed: true, + }, + "last_operation": lastOperationSchema(), + idKey: guidSchema(), + labelsKey: resourceLabelsSchema(), + annotationsKey: resourceAnnotationsSchema(), + createdAtKey: createdAtSchema(), + updatedAtKey: updatedAtSchema(), + }, + }, + }, + }, + } +} + +func (d *ServiceRouteBindingsDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + session, ok := req.ProviderData.(*managers.Session) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *managers.Session, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + d.cfClient = session.CFClient +} + +func (d *ServiceRouteBindingsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + + var data datasourceserviceRouteBindingsType + + diags := req.Config.Get(ctx, &data) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + getOptions := cfv3client.NewServiceRouteBindingListOptions() + + if !data.ServiceInstance.IsNull() { + getOptions.ServiceInstanceGUIDs = cfv3client.Filter{ + Values: []string{ + data.ServiceInstance.ValueString(), + }, + } + } + + if !data.Route.IsNull() { + getOptions.RouteGUIDs = cfv3client.Filter{ + Values: []string{ + data.Route.ValueString(), + }, + } + } + + svcRouteBindings, err := d.cfClient.ServiceRouteBindings.ListAll(ctx, getOptions) + if err != nil { + resp.Diagnostics.AddError( + "API Error Fetching Service Route Bindings.", + fmt.Sprintf("Request failed with %s.", err.Error()), + ) + return + } + + if len(svcRouteBindings) == 0 { + resp.Diagnostics.AddError( + "Unable to find any route bindings in list", + "Given input does not have any bindings present", + ) + return + } + + data.RouteBindings, diags = mapDataSourceServiceRouteBindingsValuesToType(ctx, svcRouteBindings) + resp.Diagnostics.Append(diags...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/provider/datasource_service_route_bindings_test.go b/internal/provider/datasource_service_route_bindings_test.go new file mode 100644 index 0000000..840b62f --- /dev/null +++ b/internal/provider/datasource_service_route_bindings_test.go @@ -0,0 +1,94 @@ +package provider + +import ( + "bytes" + "regexp" + "testing" + "text/template" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +type ServiceRouteBindingsModelPtr struct { + HclType string + HclObjectName string + Route *string + Routes *string + ServiceInstance *string +} + +func hclServiceRouteBindings(sip *ServiceRouteBindingsModelPtr) string { + if sip != nil { + s := ` + {{.HclType}} "cloudfoundry_service_route_bindings" {{.HclObjectName}} { + {{- if .Route}} + route = "{{.Route}}" + {{- end -}} + {{if .ServiceInstance}} + service_instance = "{{.ServiceInstance}}" + {{- end }} + {{if .Routes}} + routes = "{{.Routes}}" + {{- end }} + }` + tmpl, err := template.New("service_route_bindings").Parse(s) + if err != nil { + panic(err) + } + buf := new(bytes.Buffer) + err = tmpl.Execute(buf, sip) + if err != nil { + panic(err) + } + return buf.String() + } + return sip.HclType + ` "cloudfoundry_service_route_bindings" "` + sip.HclObjectName + ` {}` +} + +func TestServiceRouteBindingsDataSource(t *testing.T) { + var ( + ServiceInstanceGUID = "ab65cad9-73fa-4dd4-9c09-87f89b2e77ec" + ) + t.Parallel() + t.Run("happy path - read route bindings", func(t *testing.T) { + cfg := getCFHomeConf() + dataSourceName := "data.cloudfoundry_service_route_bindings.ds" + rec := cfg.SetupVCR(t, "fixtures/datasource_service_route_bindings") + defer stopQuietly(rec) + resource.Test(t, resource.TestCase{ + IsUnitTest: true, + ProtoV6ProviderFactories: getProviders(rec.GetDefaultClient()), + Steps: []resource.TestStep{ + { + Config: hclProvider(nil) + hclServiceRouteBindings(&ServiceRouteBindingsModelPtr{ + HclType: hclObjectDataSource, + HclObjectName: "ds", + ServiceInstance: &ServiceInstanceGUID, + }), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "route_bindings.#", "1"), + ), + }, + }, + }) + }) + t.Run("error path - get unavailable route bindings", func(t *testing.T) { + cfg := getCFHomeConf() + rec := cfg.SetupVCR(t, "fixtures/datasource_service_route_bindings_invalid") + defer stopQuietly(rec) + resource.UnitTest(t, resource.TestCase{ + IsUnitTest: true, + ProtoV6ProviderFactories: getProviders(rec.GetDefaultClient()), + Steps: []resource.TestStep{ + { + Config: hclProvider(nil) + hclServiceRouteBindings(&ServiceRouteBindingsModelPtr{ + HclType: hclObjectDataSource, + HclObjectName: "ds", + Route: &ServiceInstanceGUID, + }), + ExpectError: regexp.MustCompile(`Unable to find any route bindings in list`), + }, + }, + }) + }) +} diff --git a/internal/provider/fixtures/datasource_service_route_bindings.yaml b/internal/provider/fixtures/datasource_service_route_bindings.yaml new file mode 100644 index 0000000..130666e --- /dev/null +++ b/internal/provider/fixtures/datasource_service_route_bindings.yaml @@ -0,0 +1,375 @@ +--- +version: 2 +interactions: + - id: 0 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 73 + transfer_encoding: [] + trailer: {} + host: uaa.x.x.x.x.com + remote_addr: "" + request_uri: "" + body: grant_type=refresh_token&refresh_token=717a4fc677bd40218559c2c02ad83787-r + form: + grant_type: + - refresh_token + refresh_token: + - 717a4fc677bd40218559c2c02ad83787-r + headers: + Authorization: + - Basic Y2Y6 + Content-Type: + - application/x-www-form-urlencoded + url: https://uaa.x.x.x.x.com/oauth/token + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: -1 + uncompressed: false + body: '{"access_token":"redacted","token_type":"bearer","id_token":"redacted","refresh_token":"717a4fc677bd40218559c2c02ad83787-r","expires_in":1199,"scope":"cloud_controller.read password.write cloud_controller.write openid uaa.user","jti":"4660f6fc9fce490aa7722c52ae3b055f"}' + headers: + Cache-Control: + - no-store + Content-Security-Policy: + - script-src 'self' + Content-Type: + - application/json;charset=UTF-8 + Date: + - Thu, 31 Oct 2024 09:47:44 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload; + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Vcap-Request-Id: + - 7361c52b-3e20-4322-79fb-b5d0a76f1e91 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: 586.211708ms + - id: 1 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.x.x.x.x.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Authorization: + - Bearer redacted + User-Agent: + - Terraform/1.5.7 terraform-provider-cloudfoundry/dev + url: https://api.x.x.x.x.com/v3/service_route_bindings?page=1&per_page=50&service_instance_guids=ab65cad9-73fa-4dd4-9c09-87f89b2e77ec + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 1333 + uncompressed: false + body: '{"pagination":{"total_results":1,"total_pages":1,"first":{"href":"https://api.x.x.x.x.com/v3/service_route_bindings?page=1\u0026per_page=50\u0026service_instance_guids=ab65cad9-73fa-4dd4-9c09-87f89b2e77ec"},"last":{"href":"https://api.x.x.x.x.com/v3/service_route_bindings?page=1\u0026per_page=50\u0026service_instance_guids=ab65cad9-73fa-4dd4-9c09-87f89b2e77ec"},"next":null,"previous":null},"resources":[{"guid":"6961bc50-a694-4255-a5da-3ce4cdba7e54","route_service_url":"https://nginx-route-service.cfapps.sap.hana.ondemand.com","created_at":"2022-03-30T03:47:03Z","updated_at":"2022-03-30T03:47:03Z","last_operation":{"type":"create","state":"succeeded","description":"","created_at":"2022-03-30T03:47:03Z","updated_at":"2022-03-30T03:47:03Z"},"metadata":{"labels":{},"annotations":{}},"relationships":{"service_instance":{"data":{"guid":"ab65cad9-73fa-4dd4-9c09-87f89b2e77ec"}},"route":{"data":{"guid":"8c7cdbd4-98ef-4e2f-97d1-4704f435022d"}}},"links":{"self":{"href":"https://api.x.x.x.x.com/v3/service_route_bindings/6961bc50-a694-4255-a5da-3ce4cdba7e54"},"service_instance":{"href":"https://api.x.x.x.x.com/v3/service_instances/ab65cad9-73fa-4dd4-9c09-87f89b2e77ec"},"route":{"href":"https://api.x.x.x.x.com/v3/routes/8c7cdbd4-98ef-4e2f-97d1-4704f435022d"}}}]}' + headers: + Content-Length: + - "1333" + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 31 Oct 2024 09:47:45 GMT + Referrer-Policy: + - strict-origin-when-cross-origin + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload; + X-B3-Spanid: + - 72fa298d0ece342e + X-B3-Traceid: + - 1bea9912cad8466972fa298d0ece342e + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Ratelimit-Limit: + - "20000" + X-Ratelimit-Remaining: + - "18000" + X-Ratelimit-Reset: + - "1730368988" + X-Runtime: + - "0.071994" + X-Vcap-Request-Id: + - 1bea9912-cad8-4669-72fa-298d0ece342e::b17bb17a-a028-4852-ac25-90725151c621 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: 636.135459ms + - id: 2 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 73 + transfer_encoding: [] + trailer: {} + host: uaa.x.x.x.x.com + remote_addr: "" + request_uri: "" + body: grant_type=refresh_token&refresh_token=717a4fc677bd40218559c2c02ad83787-r + form: + grant_type: + - refresh_token + refresh_token: + - 717a4fc677bd40218559c2c02ad83787-r + headers: + Authorization: + - Basic Y2Y6 + Content-Type: + - application/x-www-form-urlencoded + url: https://uaa.x.x.x.x.com/oauth/token + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: -1 + uncompressed: false + body: '{"access_token":"redacted","token_type":"bearer","id_token":"redacted","refresh_token":"717a4fc677bd40218559c2c02ad83787-r","expires_in":1199,"scope":"cloud_controller.read password.write cloud_controller.write openid uaa.user","jti":"b45c55bfb3dd4a2ea005dba0a99956bf"}' + headers: + Cache-Control: + - no-store + Content-Security-Policy: + - script-src 'self' + Content-Type: + - application/json;charset=UTF-8 + Date: + - Thu, 31 Oct 2024 09:47:45 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload; + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Vcap-Request-Id: + - bc6e0b07-60bb-48a2-68ce-a4e289e6b8f4 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: 196.216416ms + - id: 3 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.x.x.x.x.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Authorization: + - Bearer redacted + User-Agent: + - Terraform/1.5.7 terraform-provider-cloudfoundry/dev + url: https://api.x.x.x.x.com/v3/service_route_bindings?page=1&per_page=50&service_instance_guids=ab65cad9-73fa-4dd4-9c09-87f89b2e77ec + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 1333 + uncompressed: false + body: '{"pagination":{"total_results":1,"total_pages":1,"first":{"href":"https://api.x.x.x.x.com/v3/service_route_bindings?page=1\u0026per_page=50\u0026service_instance_guids=ab65cad9-73fa-4dd4-9c09-87f89b2e77ec"},"last":{"href":"https://api.x.x.x.x.com/v3/service_route_bindings?page=1\u0026per_page=50\u0026service_instance_guids=ab65cad9-73fa-4dd4-9c09-87f89b2e77ec"},"next":null,"previous":null},"resources":[{"guid":"6961bc50-a694-4255-a5da-3ce4cdba7e54","route_service_url":"https://nginx-route-service.cfapps.sap.hana.ondemand.com","created_at":"2022-03-30T03:47:03Z","updated_at":"2022-03-30T03:47:03Z","last_operation":{"type":"create","state":"succeeded","description":"","created_at":"2022-03-30T03:47:03Z","updated_at":"2022-03-30T03:47:03Z"},"metadata":{"labels":{},"annotations":{}},"relationships":{"service_instance":{"data":{"guid":"ab65cad9-73fa-4dd4-9c09-87f89b2e77ec"}},"route":{"data":{"guid":"8c7cdbd4-98ef-4e2f-97d1-4704f435022d"}}},"links":{"self":{"href":"https://api.x.x.x.x.com/v3/service_route_bindings/6961bc50-a694-4255-a5da-3ce4cdba7e54"},"service_instance":{"href":"https://api.x.x.x.x.com/v3/service_instances/ab65cad9-73fa-4dd4-9c09-87f89b2e77ec"},"route":{"href":"https://api.x.x.x.x.com/v3/routes/8c7cdbd4-98ef-4e2f-97d1-4704f435022d"}}}]}' + headers: + Content-Length: + - "1333" + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 31 Oct 2024 09:47:46 GMT + Referrer-Policy: + - strict-origin-when-cross-origin + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload; + X-B3-Spanid: + - 7b5c00b45dd1fd49 + X-B3-Traceid: + - 1dda61292cce44bd7b5c00b45dd1fd49 + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Ratelimit-Limit: + - "20000" + X-Ratelimit-Remaining: + - "18000" + X-Ratelimit-Reset: + - "1730368988" + X-Runtime: + - "0.075154" + X-Vcap-Request-Id: + - 1dda6129-2cce-44bd-7b5c-00b45dd1fd49::0dfea17a-ae6d-4251-a99c-08cfb9429a7f + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: 271.719666ms + - id: 4 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 73 + transfer_encoding: [] + trailer: {} + host: uaa.x.x.x.x.com + remote_addr: "" + request_uri: "" + body: grant_type=refresh_token&refresh_token=717a4fc677bd40218559c2c02ad83787-r + form: + grant_type: + - refresh_token + refresh_token: + - 717a4fc677bd40218559c2c02ad83787-r + headers: + Authorization: + - Basic Y2Y6 + Content-Type: + - application/x-www-form-urlencoded + url: https://uaa.x.x.x.x.com/oauth/token + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: -1 + uncompressed: false + body: '{"access_token":"redacted","token_type":"bearer","id_token":"redacted","refresh_token":"717a4fc677bd40218559c2c02ad83787-r","expires_in":1199,"scope":"cloud_controller.read password.write cloud_controller.write openid uaa.user","jti":"93f8c156ab5c4faf9cfa03cb38bfb51d"}' + headers: + Cache-Control: + - no-store + Content-Security-Policy: + - script-src 'self' + Content-Type: + - application/json;charset=UTF-8 + Date: + - Thu, 31 Oct 2024 09:47:46 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload; + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Vcap-Request-Id: + - 58c0ecd3-9e43-420a-4392-2cce5e5e19e9 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: 214.750833ms + - id: 5 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.x.x.x.x.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Authorization: + - Bearer redacted + User-Agent: + - Terraform/1.5.7 terraform-provider-cloudfoundry/dev + url: https://api.x.x.x.x.com/v3/service_route_bindings?page=1&per_page=50&service_instance_guids=ab65cad9-73fa-4dd4-9c09-87f89b2e77ec + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 1333 + uncompressed: false + body: '{"pagination":{"total_results":1,"total_pages":1,"first":{"href":"https://api.x.x.x.x.com/v3/service_route_bindings?page=1\u0026per_page=50\u0026service_instance_guids=ab65cad9-73fa-4dd4-9c09-87f89b2e77ec"},"last":{"href":"https://api.x.x.x.x.com/v3/service_route_bindings?page=1\u0026per_page=50\u0026service_instance_guids=ab65cad9-73fa-4dd4-9c09-87f89b2e77ec"},"next":null,"previous":null},"resources":[{"guid":"6961bc50-a694-4255-a5da-3ce4cdba7e54","route_service_url":"https://nginx-route-service.cfapps.sap.hana.ondemand.com","created_at":"2022-03-30T03:47:03Z","updated_at":"2022-03-30T03:47:03Z","last_operation":{"type":"create","state":"succeeded","description":"","created_at":"2022-03-30T03:47:03Z","updated_at":"2022-03-30T03:47:03Z"},"metadata":{"labels":{},"annotations":{}},"relationships":{"service_instance":{"data":{"guid":"ab65cad9-73fa-4dd4-9c09-87f89b2e77ec"}},"route":{"data":{"guid":"8c7cdbd4-98ef-4e2f-97d1-4704f435022d"}}},"links":{"self":{"href":"https://api.x.x.x.x.com/v3/service_route_bindings/6961bc50-a694-4255-a5da-3ce4cdba7e54"},"service_instance":{"href":"https://api.x.x.x.x.com/v3/service_instances/ab65cad9-73fa-4dd4-9c09-87f89b2e77ec"},"route":{"href":"https://api.x.x.x.x.com/v3/routes/8c7cdbd4-98ef-4e2f-97d1-4704f435022d"}}}]}' + headers: + Content-Length: + - "1333" + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 31 Oct 2024 09:47:46 GMT + Referrer-Policy: + - strict-origin-when-cross-origin + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload; + X-B3-Spanid: + - 53db9b7542208165 + X-B3-Traceid: + - 2caaa15ed3f8407353db9b7542208165 + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Ratelimit-Limit: + - "20000" + X-Ratelimit-Remaining: + - "18000" + X-Ratelimit-Reset: + - "1730368988" + X-Runtime: + - "0.075181" + X-Vcap-Request-Id: + - 2caaa15e-d3f8-4073-53db-9b7542208165::8a77828b-43de-4804-8d7c-086cd44a6f11 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: 262.409084ms diff --git a/internal/provider/fixtures/datasource_service_route_bindings_invalid.yaml b/internal/provider/fixtures/datasource_service_route_bindings_invalid.yaml new file mode 100644 index 0000000..1497239 --- /dev/null +++ b/internal/provider/fixtures/datasource_service_route_bindings_invalid.yaml @@ -0,0 +1,127 @@ +--- +version: 2 +interactions: + - id: 0 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 73 + transfer_encoding: [] + trailer: {} + host: uaa.x.x.x.x.com + remote_addr: "" + request_uri: "" + body: grant_type=refresh_token&refresh_token=717a4fc677bd40218559c2c02ad83787-r + form: + grant_type: + - refresh_token + refresh_token: + - 717a4fc677bd40218559c2c02ad83787-r + headers: + Authorization: + - Basic Y2Y6 + Content-Type: + - application/x-www-form-urlencoded + url: https://uaa.x.x.x.x.com/oauth/token + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: -1 + uncompressed: false + body: '{"access_token":"redacted","token_type":"bearer","id_token":"redacted","refresh_token":"717a4fc677bd40218559c2c02ad83787-r","expires_in":1199,"scope":"cloud_controller.read password.write cloud_controller.write openid uaa.user","jti":"4c0d0ca92cdc495ba5499b07f603f8ca"}' + headers: + Cache-Control: + - no-store + Content-Security-Policy: + - script-src 'self' + Content-Type: + - application/json;charset=UTF-8 + Date: + - Thu, 31 Oct 2024 09:47:46 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload; + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Vcap-Request-Id: + - 1275eeb8-e5dc-485f-628b-e0b97b85e724 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: 190.330417ms + - id: 1 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.x.x.x.x.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Authorization: + - Bearer redacted + User-Agent: + - Terraform/1.5.7 terraform-provider-cloudfoundry/dev + url: https://api.x.x.x.x.com/v3/service_route_bindings?page=1&per_page=50&route_guids=ab65cad9-73fa-4dd4-9c09-87f89b2e77ec + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 412 + uncompressed: false + body: '{"pagination":{"total_results":0,"total_pages":1,"first":{"href":"https://api.x.x.x.x.com/v3/service_route_bindings?page=1\u0026per_page=50\u0026route_guids=ab65cad9-73fa-4dd4-9c09-87f89b2e77ec"},"last":{"href":"https://api.x.x.x.x.com/v3/service_route_bindings?page=1\u0026per_page=50\u0026route_guids=ab65cad9-73fa-4dd4-9c09-87f89b2e77ec"},"next":null,"previous":null},"resources":[]}' + headers: + Content-Length: + - "412" + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 31 Oct 2024 09:47:47 GMT + Referrer-Policy: + - strict-origin-when-cross-origin + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload; + X-B3-Spanid: + - 4a32f985954a6157 + X-B3-Traceid: + - 65170c33b40743db4a32f985954a6157 + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Ratelimit-Limit: + - "20000" + X-Ratelimit-Remaining: + - "18000" + X-Ratelimit-Reset: + - "1730368988" + X-Runtime: + - "0.065372" + X-Vcap-Request-Id: + - 65170c33-b407-43db-4a32-f985954a6157::6a357e62-04ee-4f74-8682-8a8e026d1d76 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: 251.341167ms diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 8ddf5a0..1e1e9b0 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -344,6 +344,7 @@ func (p *CloudFoundryProvider) DataSources(ctx context.Context) []func() datasou NewDomainsDataSource, NewRoutesDataSource, NewServiceBrokerDataSource, + NewServiceRouteBindingsDataSource, } } diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index b6b23a3..3808bc7 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -323,6 +323,7 @@ func TestProvider_HasDataSources(t *testing.T) { "cloudfoundry_domains", "cloudfoundry_routes", "cloudfoundry_service_broker", + "cloudfoundry_service_route_bindings", } ctx := context.Background() diff --git a/internal/provider/types_service_route_binding.go b/internal/provider/types_service_route_binding.go index f550f00..4db59dd 100644 --- a/internal/provider/types_service_route_binding.go +++ b/internal/provider/types_service_route_binding.go @@ -24,6 +24,24 @@ type serviceRouteBindingType struct { LastOperation types.Object `tfsdk:"last_operation"` //LastOperationType } +type datasourceserviceRouteBindingType struct { + ID types.String `tfsdk:"id"` + Route types.String `tfsdk:"route"` + RouteServiceURL types.String `tfsdk:"route_service_url"` + ServiceInstance types.String `tfsdk:"service_instance"` + Labels types.Map `tfsdk:"labels"` + Annotations types.Map `tfsdk:"annotations"` + CreatedAt types.String `tfsdk:"created_at"` + UpdatedAt types.String `tfsdk:"updated_at"` + LastOperation types.Object `tfsdk:"last_operation"` //LastOperationType +} + +type datasourceserviceRouteBindingsType struct { + ServiceInstance types.String `tfsdk:"service_instance"` + Route types.String `tfsdk:"route"` + RouteBindings []datasourceserviceRouteBindingType `tfsdk:"route_bindings"` +} + func mapServiceRouteBindingValuesToType(ctx context.Context, value *resource.ServiceRouteBinding) (serviceRouteBindingType, diag.Diagnostics) { var diags, diagnostics diag.Diagnostics serviceRouteBinding := serviceRouteBindingType{ @@ -78,3 +96,22 @@ func (plan *serviceRouteBindingType) mapUpdateServiceRouteBindingTypeToValues(ct return *updateRouteBinding, diagnostics } + +func (a *serviceRouteBindingType) Reduce() datasourceserviceRouteBindingType { + var reduced datasourceserviceRouteBindingType + copyFields(&reduced, a) + return reduced +} + +func mapDataSourceServiceRouteBindingsValuesToType(ctx context.Context, svcRouteBindings []*resource.ServiceRouteBinding) ([]datasourceserviceRouteBindingType, diag.Diagnostics) { + var diagnostics diag.Diagnostics + + svcRouteBindingsList := []datasourceserviceRouteBindingType{} + for _, svcRouteBinding := range svcRouteBindings { + bindingValue, diags := mapServiceRouteBindingValuesToType(ctx, svcRouteBinding) + diagnostics.Append(diags...) + svcRouteBindingsList = append(svcRouteBindingsList, bindingValue.Reduce()) + } + + return svcRouteBindingsList, diagnostics +} diff --git a/migration-guide/Readme.md b/migration-guide/Readme.md index ba69c03..f839a63 100644 --- a/migration-guide/Readme.md +++ b/migration-guide/Readme.md @@ -205,6 +205,7 @@ The below mentioned dataSources have been newly added in the current provider. - [Service Broker](../docs/data-sources/service_broker.md) - [Service Instances](../docs/data-sources/service_instances.md) - [Service Plans](../docs/data-sources/service_plans.md) +- [Service Route Bindings](../docs/data-sources/service_route_bindings.md) - [Space Role](../docs/data-sources/space_role.md) - [Spaces](../docs/data-sources/spaces.md) - [Space Roles](../docs/data-sources/space_roles.md)