From 9333f226be737675df2d669673bfa076635bca70 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 14 Jun 2022 15:45:31 +0100 Subject: [PATCH 01/10] Migrating to terraform-plugin-framework (#93) --- go.mod | 7 +- go.sum | 19 +- internal/provider/data_source.go | 165 ------------- internal/provider/data_source_http.go | 221 ++++++++++++++++++ ...ource_test.go => data_source_http_test.go} | 12 +- internal/provider/provider.go | 33 ++- internal/provider/provider_test.go | 21 +- main.go | 18 +- 8 files changed, 291 insertions(+), 205 deletions(-) delete mode 100644 internal/provider/data_source.go create mode 100644 internal/provider/data_source_http.go rename internal/provider/{data_source_test.go => data_source_http_test.go} (93%) diff --git a/go.mod b/go.mod index bde5090e..fab3c1fc 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,8 @@ module github.com/terraform-providers/terraform-provider-http go 1.17 require ( - github.com/hashicorp/terraform-plugin-docs v0.9.0 + github.com/hashicorp/terraform-plugin-docs v0.10.0 + github.com/hashicorp/terraform-plugin-framework v0.8.1-0.20220613174400-51e06ca1477f github.com/hashicorp/terraform-plugin-sdk/v2 v2.17.0 ) @@ -24,7 +25,7 @@ require ( github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect - github.com/hashicorp/go-hclog v1.2.0 // indirect + github.com/hashicorp/go-hclog v1.2.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.4.4 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect @@ -35,7 +36,7 @@ require ( github.com/hashicorp/terraform-exec v0.16.1 // indirect github.com/hashicorp/terraform-json v0.14.0 // indirect github.com/hashicorp/terraform-plugin-go v0.9.1 // indirect - github.com/hashicorp/terraform-plugin-log v0.4.0 // indirect + github.com/hashicorp/terraform-plugin-log v0.4.1 // indirect github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896 // indirect github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect diff --git a/go.sum b/go.sum index d47cdd4c..fc24f417 100644 --- a/go.sum +++ b/go.sum @@ -121,8 +121,9 @@ github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/S github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw= +github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= @@ -148,12 +149,15 @@ github.com/hashicorp/terraform-exec v0.16.1/go.mod h1:aj0lVshy8l+MHhFNoijNHtqTJQ github.com/hashicorp/terraform-json v0.13.0/go.mod h1:y5OdLBCT+rxbwnpxZs9kGL7R9ExU76+cpdY8zHwoazk= github.com/hashicorp/terraform-json v0.14.0 h1:sh9iZ1Y8IFJLx+xQiKHGud6/TSUCM0N8e17dKDpqV7s= github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM= -github.com/hashicorp/terraform-plugin-docs v0.9.0 h1:CEu7NToNWRR2os6DfT/Du2s+8qzXHyIcZQ10oiMdbJs= -github.com/hashicorp/terraform-plugin-docs v0.9.0/go.mod h1:47ZcsxMUJxAjGzHf+dZ9q78oYf4PeJxO1N+i5XDtXBc= +github.com/hashicorp/terraform-plugin-docs v0.10.0 h1:LwoFJ3RoKElDFhBRomsDkaEn59OUNXa92M21VCuL7Vk= +github.com/hashicorp/terraform-plugin-docs v0.10.0/go.mod h1:47ZcsxMUJxAjGzHf+dZ9q78oYf4PeJxO1N+i5XDtXBc= +github.com/hashicorp/terraform-plugin-framework v0.8.1-0.20220613174400-51e06ca1477f h1:oOvR3qwrQ98aVdjf8AfHTlHnN5af6ZyCtRXC3xrNfPg= +github.com/hashicorp/terraform-plugin-framework v0.8.1-0.20220613174400-51e06ca1477f/go.mod h1:ActelD2V6yt2m0MwIX4jESGDYJ573rAvZswGjSGm1rY= github.com/hashicorp/terraform-plugin-go v0.9.1 h1:vXdHaQ6aqL+OF076nMSBV+JKPdmXlzG5mzVDD04WyPs= github.com/hashicorp/terraform-plugin-go v0.9.1/go.mod h1:ItjVSlQs70otlzcCwlPcU8FRXLdO973oYFRZwAOxy8M= -github.com/hashicorp/terraform-plugin-log v0.4.0 h1:F3eVnm8r2EfQCe2k9blPIiF/r2TT01SHijXnS7bujvc= github.com/hashicorp/terraform-plugin-log v0.4.0/go.mod h1:9KclxdunFownr4pIm1jdmwKRmE4d6HVG2c9XDq47rpg= +github.com/hashicorp/terraform-plugin-log v0.4.1 h1:xpbmVhvuU3mgHzLetOmx9pkOL2rmgpu302XxddON6eo= +github.com/hashicorp/terraform-plugin-log v0.4.1/go.mod h1:p4R1jWBXRTvL4odmEkFfDdhUjHf9zcs/BCoNHAc7IK4= github.com/hashicorp/terraform-plugin-sdk/v2 v2.17.0 h1:Qr5fWNg1SPSfCRMtou67Y6Kcy9UnMYRNlIJTKRuUvXU= github.com/hashicorp/terraform-plugin-sdk/v2 v2.17.0/go.mod h1:b+LFg8WpYgFgvEBP/6Htk5H9/pJp1V1E8NJAekfH2Ws= github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896 h1:1FGtlkJw87UsTMg5s8jrekrHmUPUJaMcu6ELiVhQrNw= @@ -257,8 +261,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= @@ -345,6 +350,7 @@ golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -418,7 +424,8 @@ gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/provider/data_source.go b/internal/provider/data_source.go deleted file mode 100644 index 75fc8508..00000000 --- a/internal/provider/data_source.go +++ /dev/null @@ -1,165 +0,0 @@ -package provider - -import ( - "context" - "fmt" - "io/ioutil" - "mime" - "net/http" - "regexp" - "strings" - - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func dataSource() *schema.Resource { - return &schema.Resource{ - Description: ` -The ` + "`http`" + ` data source makes an HTTP GET request to the given URL and exports -information about the response. - -The given URL may be either an ` + "`http`" + ` or ` + "`https`" + ` URL. At present this resource -can only retrieve data from URLs that respond with ` + "`text/*`" + ` or -` + "`application/json`" + ` content types, and expects the result to be UTF-8 encoded -regardless of the returned content type header. - -~> **Important** Although ` + "`https`" + ` URLs can be used, there is currently no -mechanism to authenticate the remote server except for general verification of -the server certificate's chain of trust. Data retrieved from servers not under -your control should be treated as untrustworthy.`, - ReadContext: dataSourceRead, - - Schema: map[string]*schema.Schema{ - "url": { - Description: "The URL for the request. Supported schemes are `http` and `https`.", - Type: schema.TypeString, - Required: true, - }, - - "request_headers": { - Description: "A map of request header field names and values.", - Type: schema.TypeMap, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - - "body": { - Description: "The response body returned as a string. " + - "**NOTE**: This is deprecated, use `response_body` instead.", - Type: schema.TypeString, - Computed: true, - Deprecated: "Use response_body instead", - }, - - "response_body": { - Description: "The response body returned as a string.", - Type: schema.TypeString, - Computed: true, - }, - - "response_headers": { - Description: `A map of response header field names and values.` + - ` Duplicate headers are concatenated with according to [RFC2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2).`, - Type: schema.TypeMap, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - }, - } -} - -func dataSourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) (diags diag.Diagnostics) { - url := d.Get("url").(string) - headers := d.Get("request_headers").(map[string]interface{}) - - client := &http.Client{} - - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) - if err != nil { - return append(diags, diag.Errorf("Error creating request: %s", err)...) - } - - for name, value := range headers { - req.Header.Set(name, value.(string)) - } - - resp, err := client.Do(req) - if err != nil { - return append(diags, diag.Errorf("Error making request: %s", err)...) - } - - defer resp.Body.Close() - - if resp.StatusCode != 200 { - return append(diags, diag.Errorf("HTTP request error. Response code: %d", resp.StatusCode)...) - } - - contentType := resp.Header.Get("Content-Type") - if !isContentTypeText(contentType) { - diags = append(diags, diag.Diagnostic{ - Severity: diag.Warning, - Summary: fmt.Sprintf("Content-Type is not recognized as a text type, got %q", contentType), - Detail: "If the content is binary data, Terraform may not properly handle the contents of the response.", - }) - } - - bytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - - responseHeaders := make(map[string]string) - for k, v := range resp.Header { - // Concatenate according to RFC2616 - // cf. https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 - responseHeaders[k] = strings.Join(v, ", ") - } - - if err = d.Set("body", string(bytes)); err != nil { - return append(diags, diag.Errorf("Error setting HTTP response body: %s", err)...) - } - - if err = d.Set("response_body", string(bytes)); err != nil { - return append(diags, diag.Errorf("Error setting HTTP response body: %s", err)...) - } - - if err = d.Set("response_headers", responseHeaders); err != nil { - return append(diags, diag.Errorf("Error setting HTTP response headers: %s", err)...) - } - - // set ID as something more stable than time - d.SetId(url) - - return diags -} - -// This is to prevent potential issues w/ binary files -// and generally unprintable characters -// See https://github.com/hashicorp/terraform/pull/3858#issuecomment-156856738 -func isContentTypeText(contentType string) bool { - - parsedType, params, err := mime.ParseMediaType(contentType) - if err != nil { - return false - } - - allowedContentTypes := []*regexp.Regexp{ - regexp.MustCompile("^text/.+"), - regexp.MustCompile("^application/json$"), - regexp.MustCompile(`^application/samlmetadata\+xml`), - } - - for _, r := range allowedContentTypes { - if r.MatchString(parsedType) { - charset := strings.ToLower(params["charset"]) - return charset == "" || charset == "utf-8" || charset == "us-ascii" - } - } - - return false -} diff --git a/internal/provider/data_source_http.go b/internal/provider/data_source_http.go new file mode 100644 index 00000000..c40e88a5 --- /dev/null +++ b/internal/provider/data_source_http.go @@ -0,0 +1,221 @@ +package provider + +import ( + "context" + "fmt" + "io/ioutil" + "mime" + "net/http" + "regexp" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type dataSourceHTTPType struct { +} + +func (d dataSourceHTTPType) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + Description: ` +The ` + "`http`" + ` data source makes an HTTP GET request to the given URL and exports +information about the response. + +The given URL may be either an ` + "`http`" + ` or ` + "`https`" + ` URL. At present this resource +can only retrieve data from URLs that respond with ` + "`text/*`" + ` or +` + "`application/json`" + ` content types, and expects the result to be UTF-8 encoded +regardless of the returned content type header. + +~> **Important** Although ` + "`https`" + ` URLs can be used, there is currently no +mechanism to authenticate the remote server except for general verification of +the server certificate's chain of trust. Data retrieved from servers not under +your control should be treated as untrustworthy.`, + + Attributes: map[string]tfsdk.Attribute{ + "url": { + Description: "The URL for the request. Supported schemes are `http` and `https`.", + Type: types.StringType, + Required: true, + }, + + "request_headers": { + Description: "A map of request header field names and values.", + Type: types.MapType{ + ElemType: types.StringType, + }, + Optional: true, + }, + + "body": { + Description: "The response body returned as a string. " + + "**NOTE**: This is deprecated, use `response_body` instead.", + Type: types.StringType, + Computed: true, + DeprecationMessage: "**NOTE**: This is deprecated, use `response_body` instead.", + }, + + "response_body": { + Description: "The response body returned as a string.", + Type: types.StringType, + Computed: true, + }, + + "response_headers": { + Description: `A map of response header field names and values.` + + ` Duplicate headers are concatenated with according to [RFC2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2).`, + Type: types.MapType{ + ElemType: types.StringType, + }, + Computed: true, + }, + + "id": { + Description: "The ID of this resource.", + Type: types.StringType, + Computed: true, + }, + }, + }, nil +} + +func (d dataSourceHTTPType) NewDataSource(ctx context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { + return dataSourceHTTP{ + p: *(p.(*provider)), + }, nil +} + +type dataSourceHTTP struct { + p provider +} + +type HTTPModel struct { + ID types.String `tfsdk:"id"` + URL types.String `tfsdk:"url"` + RequestHeaders types.Map `tfsdk:"request_headers"` + ResponseHeaders types.Map `tfsdk:"response_headers"` + Body types.String `tfsdk:"body"` + ResponseBody types.String `tfsdk:"response_body"` +} + +func (d dataSourceHTTP) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { + var model HTTPModel + diags := req.Config.Get(ctx, &model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + url := model.URL.Value + headers := model.RequestHeaders + + client := &http.Client{} + + request, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + resp.Diagnostics.AddError( + "Error creating request", + fmt.Sprintf("Error creating request: %s", err), + ) + return + } + + for name, value := range headers.Elems { + var header string + diags = tfsdk.ValueAs(ctx, value, &header) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + request.Header.Set(name, header) + } + + response, err := client.Do(request) + if err != nil { + resp.Diagnostics.AddError( + "Error making request", + fmt.Sprintf("Error making request: %s", err), + ) + return + } + + defer response.Body.Close() + + if response.StatusCode != 200 { + resp.Diagnostics.AddError( + "HTTP response code error", + fmt.Sprintf("Response code is not 200: %d", response.StatusCode), + ) + return + } + + contentType := response.Header.Get("Content-Type") + if !isContentTypeText(contentType) { + resp.Diagnostics.AddWarning( + fmt.Sprintf("Content-Type is not recognized as a text type, got %q", contentType), + "If the content is binary data, Terraform may not properly handle the contents of the response.", + ) + } + + bytes, err := ioutil.ReadAll(response.Body) + if err != nil { + resp.Diagnostics.AddError( + "Error reading response body", + fmt.Sprintf("Error reading response body: %s", err), + ) + return + } + + responseBody := string(bytes) + + responseHeaders := make(map[string]string) + for k, v := range response.Header { + // Concatenate according to RFC2616 + // cf. https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 + responseHeaders[k] = strings.Join(v, ", ") + } + + respHeadersState := types.Map{} + + diags = tfsdk.ValueFrom(ctx, responseHeaders, types.Map{ElemType: types.StringType}.Type(ctx), &respHeadersState) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + model.ID = types.String{Value: url} + model.ResponseHeaders = respHeadersState + model.Body = types.String{Value: responseBody} + model.ResponseBody = types.String{Value: responseBody} + + diags = resp.State.Set(ctx, model) + resp.Diagnostics.Append(diags...) +} + +// This is to prevent potential issues w/ binary files +// and generally unprintable characters +// See https://github.com/hashicorp/terraform/pull/3858#issuecomment-156856738 +func isContentTypeText(contentType string) bool { + + parsedType, params, err := mime.ParseMediaType(contentType) + if err != nil { + return false + } + + allowedContentTypes := []*regexp.Regexp{ + regexp.MustCompile("^text/.+"), + regexp.MustCompile("^application/json$"), + regexp.MustCompile(`^application/samlmetadata\+xml`), + } + + for _, r := range allowedContentTypes { + if r.MatchString(parsedType) { + charset := strings.ToLower(params["charset"]) + return charset == "" || charset == "utf-8" || charset == "us-ascii" + } + } + + return false +} diff --git a/internal/provider/data_source_test.go b/internal/provider/data_source_http_test.go similarity index 93% rename from internal/provider/data_source_test.go rename to internal/provider/data_source_http_test.go index 8c032a67..c002fd0d 100644 --- a/internal/provider/data_source_test.go +++ b/internal/provider/data_source_http_test.go @@ -16,7 +16,7 @@ func TestDataSource_http200(t *testing.T) { defer testHttpMock.server.Close() resource.UnitTest(t, resource.TestCase{ - ProviderFactories: testProviders(), + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), Steps: []resource.TestStep{ { Config: fmt.Sprintf(testDataSourceConfigBasic, testHttpMock.server.URL, 200), @@ -37,11 +37,11 @@ func TestDataSource_http404(t *testing.T) { defer testHttpMock.server.Close() resource.UnitTest(t, resource.TestCase{ - ProviderFactories: testProviders(), + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), Steps: []resource.TestStep{ { Config: fmt.Sprintf(testDataSourceConfigBasic, testHttpMock.server.URL, 404), - ExpectError: regexp.MustCompile("HTTP request error. Response code: 404"), + ExpectError: regexp.MustCompile("Response code is not 200: 404"), }, }, }) @@ -53,7 +53,7 @@ func TestDataSource_withHeaders200(t *testing.T) { defer testHttpMock.server.Close() resource.UnitTest(t, resource.TestCase{ - ProviderFactories: testProviders(), + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), Steps: []resource.TestStep{ { Config: fmt.Sprintf(testDataSourceConfigWithHeaders, testHttpMock.server.URL, 200), @@ -72,7 +72,7 @@ func TestDataSource_utf8(t *testing.T) { defer testHttpMock.server.Close() resource.UnitTest(t, resource.TestCase{ - ProviderFactories: testProviders(), + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), Steps: []resource.TestStep{ { Config: fmt.Sprintf(testDataSourceConfigUTF8, testHttpMock.server.URL, 200), @@ -91,7 +91,7 @@ func TestDataSource_utf16(t *testing.T) { defer testHttpMock.server.Close() resource.UnitTest(t, resource.TestCase{ - ProviderFactories: testProviders(), + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), Steps: []resource.TestStep{ { Config: fmt.Sprintf(testDataSourceConfigUTF16, testHttpMock.server.URL, 200), diff --git a/internal/provider/provider.go b/internal/provider/provider.go index a9da1e44..e6c60ba0 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1,17 +1,32 @@ package provider import ( - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) -func New() *schema.Provider { - return &schema.Provider{ - Schema: map[string]*schema.Schema{}, +type provider struct { +} - DataSourcesMap: map[string]*schema.Resource{ - "http": dataSource(), - }, +func New() tfsdk.Provider { + return &provider{} +} + +func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{}, nil +} + +func (p *provider) Configure(_ context.Context, _ tfsdk.ConfigureProviderRequest, _ *tfsdk.ConfigureProviderResponse) { +} + +func (p *provider) GetResources(_ context.Context) (map[string]tfsdk.ResourceType, diag.Diagnostics) { + return map[string]tfsdk.ResourceType{}, nil +} - ResourcesMap: map[string]*schema.Resource{}, - } +func (p *provider) GetDataSources(ctx context.Context) (map[string]tfsdk.DataSourceType, diag.Diagnostics) { + return map[string]tfsdk.DataSourceType{ + "http": dataSourceHTTPType{}, + }, nil } diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index 8d591fda..05d4400b 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -1,20 +1,15 @@ package provider import ( - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-framework/providerserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" ) -//nolint:unparam // error is always nil -func testProviders() map[string]func() (*schema.Provider, error) { - return map[string]func() (*schema.Provider, error){ - "http": func() (*schema.Provider, error) { return New(), nil }, - } -} - -func TestProvider(t *testing.T) { - if err := New().InternalValidate(); err != nil { - t.Fatalf("err: %s", err) +//nolint:unparam +func testAccProtoV6ProviderFactories() map[string]func() (tfprotov6.ProviderServer, error) { + return map[string]func() (tfprotov6.ProviderServer, error){ + "http": func() (tfprotov6.ProviderServer, error) { + return providerserver.NewProtocol6(New())(), nil + }, } } diff --git a/main.go b/main.go index 22da7b58..8ab18824 100644 --- a/main.go +++ b/main.go @@ -1,16 +1,28 @@ package main import ( - "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" + "context" + "log" + "github.com/hashicorp/terraform-plugin-framework/providerserver" "github.com/terraform-providers/terraform-provider-http/internal/provider" ) +// Run "go generate" to format example terraform files and generate the docs for the registry/website + +// If you do not have terraform installed, you can remove the formatting command, but its suggested to +// ensure the documentation is formatted properly. +//go:generate terraform fmt -recursive ./examples/ + // Run the docs generation tool, check its repository for more information on how it works and how docs // can be customized. //go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs func main() { - plugin.Serve(&plugin.ServeOpts{ - ProviderFunc: provider.New}) + err := providerserver.Serve(context.Background(), provider.New, providerserver.ServeOpts{ + Address: "registry.terraform.io/hashicorp/http", + }) + if err != nil { + log.Fatal(err) + } } From df26fbfd507a881ff94f99849433e1aea14857f8 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 14 Jun 2022 16:03:53 +0100 Subject: [PATCH 02/10] Only test TF >= 1.0.* (#93) --- .github/workflows/test.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6282e995..4bfcc686 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -59,12 +59,9 @@ jobs: - windows-latest - ubuntu-latest terraform: - - '0.12.*' - - '0.13.*' - - '0.14.*' - - '0.15.*' - '1.0.*' - '1.1.*' + - '1.2.*' steps: - name: Setup Go From 543834845924192681cff3ce9892a4f1f1e718ab Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 14 Jun 2022 16:14:16 +0100 Subject: [PATCH 03/10] Removing body attribute and updating tests (#93) --- internal/provider/data_source_http.go | 10 --- internal/provider/data_source_http_test.go | 72 +++++++--------------- 2 files changed, 23 insertions(+), 59 deletions(-) diff --git a/internal/provider/data_source_http.go b/internal/provider/data_source_http.go index c40e88a5..061c9a31 100644 --- a/internal/provider/data_source_http.go +++ b/internal/provider/data_source_http.go @@ -48,14 +48,6 @@ your control should be treated as untrustworthy.`, Optional: true, }, - "body": { - Description: "The response body returned as a string. " + - "**NOTE**: This is deprecated, use `response_body` instead.", - Type: types.StringType, - Computed: true, - DeprecationMessage: "**NOTE**: This is deprecated, use `response_body` instead.", - }, - "response_body": { Description: "The response body returned as a string.", Type: types.StringType, @@ -95,7 +87,6 @@ type HTTPModel struct { URL types.String `tfsdk:"url"` RequestHeaders types.Map `tfsdk:"request_headers"` ResponseHeaders types.Map `tfsdk:"response_headers"` - Body types.String `tfsdk:"body"` ResponseBody types.String `tfsdk:"response_body"` } @@ -187,7 +178,6 @@ func (d dataSourceHTTP) Read(ctx context.Context, req tfsdk.ReadDataSourceReques model.ID = types.String{Value: url} model.ResponseHeaders = respHeadersState - model.Body = types.String{Value: responseBody} model.ResponseBody = types.String{Value: responseBody} diags = resp.State.Set(ctx, model) diff --git a/internal/provider/data_source_http_test.go b/internal/provider/data_source_http_test.go index c002fd0d..4107b5de 100644 --- a/internal/provider/data_source_http_test.go +++ b/internal/provider/data_source_http_test.go @@ -21,7 +21,6 @@ func TestDataSource_http200(t *testing.T) { { Config: fmt.Sprintf(testDataSourceConfigBasic, testHttpMock.server.URL, 200), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("data.http.http_test", "body", "1.0.0"), resource.TestCheckResourceAttr("data.http.http_test", "response_body", "1.0.0"), resource.TestCheckResourceAttr("data.http.http_test", "response_headers.X-Single", "foobar"), resource.TestCheckResourceAttr("data.http.http_test", "response_headers.X-Double", "1, 2"), @@ -58,7 +57,6 @@ func TestDataSource_withHeaders200(t *testing.T) { { Config: fmt.Sprintf(testDataSourceConfigWithHeaders, testHttpMock.server.URL, 200), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("data.http.http_test", "body", "1.0.0"), resource.TestCheckResourceAttr("data.http.http_test", "response_body", "1.0.0"), ), }, @@ -77,7 +75,6 @@ func TestDataSource_utf8(t *testing.T) { { Config: fmt.Sprintf(testDataSourceConfigUTF8, testHttpMock.server.URL, 200), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("data.http.http_test", "body", "1.0.0"), resource.TestCheckResourceAttr("data.http.http_test", "response_body", "1.0.0"), ), }, @@ -102,52 +99,23 @@ func TestDataSource_utf16(t *testing.T) { }) } -// TODO: This test fails under Terraform 0.14. It should be uncommented when we -// are able to include Terraform version logic within acceptance tests, or when -// 0.14 is removed from the test matrix. -// See https://github.com/hashicorp/terraform-provider-http/pull/74 -// -// const testDataSourceConfig_x509cert = ` -// data "http" "http_test" { -// url = "%s/x509/cert.pem" -// } - -// output "body" { -// value = "${data.http.http_test.body}" -// } -// ` - -// func TestDataSource_x509cert(t *testing.T) { -// testHttpMock := setUpMockHttpServer() - -// defer testHttpMock.server.Close() - -// resource.UnitTest(t, resource.TestCase{ -// Providers: testProviders, -// Steps: []resource.TestStep{ -// { -// Config: fmt.Sprintf(testDataSourceConfig_x509cert, testHttpMock.server.URL), -// Check: func(s *terraform.State) error { -// _, ok := s.RootModule().Resources["data.http.http_test"] -// if !ok { -// return fmt.Errorf("missing data resource") -// } - -// outputs := s.RootModule().Outputs - -// if outputs["body"].Value != "pem" { -// return fmt.Errorf( -// `'body' output is %s; want 'pem'`, -// outputs["body"].Value, -// ) -// } - -// return nil -// }, -// }, -// }, -// }) -// } +func TestDataSource_x509cert(t *testing.T) { + testHttpMock := setUpMockHttpServer() + + defer testHttpMock.server.Close() + + resource.UnitTest(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(testDataSourceConfigx509cert, testHttpMock.server.URL), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.http.http_test", "response_body", "pem"), + ), + }, + }, + }) +} const testDataSourceConfigBasic = ` data "http" "http_test" { @@ -177,6 +145,12 @@ data "http" "http_test" { } ` +const testDataSourceConfigx509cert = ` +data "http" "http_test" { + url = "%s/x509/cert.pem" +} +` + type TestHttpMock struct { server *httptest.Server } From 3d5fb8d5b883294856e9aab46d510097d94181d9 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 14 Jun 2022 17:12:24 +0100 Subject: [PATCH 04/10] Removing status code check and adding status_code attribute (#93) --- CHANGELOG.md | 9 ++ docs/data-sources/http.md | 55 +++++++- examples/data-sources/http/postcondition.tf | 15 +++ examples/data-sources/http/precondition.tf | 17 +++ go.mod | 2 +- go.sum | 4 +- internal/provider/data_source_http.go | 18 +-- internal/provider/data_source_http_test.go | 137 +++++++++++--------- internal/provider/provider.go | 2 +- templates/data-sources/http.md.tmpl | 30 +++++ 10 files changed, 213 insertions(+), 76 deletions(-) create mode 100644 examples/data-sources/http/postcondition.tf create mode 100644 examples/data-sources/http/precondition.tf create mode 100644 templates/data-sources/http.md.tmpl diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ee93140..4549164d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 3.0.0 (unreleased) + +BREAKING CHANGES: + +* data-source/http: Provider has been migrated from SDKv2 to Framework ([#142](https://github.com/hashicorp/terraform-provider-http/pull/142)). +* data-source/http: There is no longer a check that the status code is 200 following a request. `status_code` attribute has been added and should be used in + [precondition and postcondition](https://www.terraform.io/language/expressions/custom-conditions) checks instead ([114](https://github.com/hashicorp/terraform-provider-http/pull/114)). +* data-source/http: `body` has been removed ([#137](https://github.com/hashicorp/terraform-provider-http/pull/137)). + ## 2.2.0 (June 02, 2022) ENHANCEMENTS: diff --git a/docs/data-sources/http.md b/docs/data-sources/http.md index c2819df1..0fa557d7 100644 --- a/docs/data-sources/http.md +++ b/docs/data-sources/http.md @@ -1,5 +1,4 @@ --- -# generated by https://github.com/hashicorp/terraform-plugin-docs page_title: "http Data Source - terraform-provider-http" subcategory: "" description: |- @@ -45,6 +44,54 @@ data "http" "example" { } ``` +## Usage with Postcondition + +[Precondition and Postcondition](https://www.terraform.io/language/expressions/custom-conditions) +checks are available with Terraform v1.2.0 and later. + +```terraform +data "http" "example" { + url = "https://checkpoint-api.hashicorp.com/v1/check/terraform" + + # Optional request headers + request_headers = { + Accept = "application/json" + } + + lifecycle { + postcondition { + condition = contains([201, 204], self.status_code) + error_message = "Status code invalid" + } + } +} +``` + +## Usage with Precondition + +[Precondition and Postcondition](https://www.terraform.io/language/expressions/custom-conditions) +checks are available with Terraform v1.2.0 and later. + +```terraform +data "http" "example" { + url = "https://checkpoint-api.hashicorp.com/v1/check/terraform" + + # Optional request headers + request_headers = { + Accept = "application/json" + } +} + +resource "random_uuid" "example" { + lifecycle { + precondition { + condition = contains([201, 204], data.http.example.status_code) + error_message = "Status code invalid" + } + } +} +``` + ## Schema @@ -58,9 +105,7 @@ data "http" "example" { ### Read-Only -- `body` (String, Deprecated) The response body returned as a string. **NOTE**: This is deprecated, use `response_body` instead. - `id` (String) The ID of this resource. - `response_body` (String) The response body returned as a string. -- `response_headers` (Map of String) A map of response header field names and values. Duplicate headers are concatenated with according to [RFC2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2). - - +- `response_headers` (Map of String) A map of response header field names and values. Duplicate headers are concatenated according to [RFC2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2). +- `status_code` (Number) The HTTP response status code. diff --git a/examples/data-sources/http/postcondition.tf b/examples/data-sources/http/postcondition.tf new file mode 100644 index 00000000..00f13dd4 --- /dev/null +++ b/examples/data-sources/http/postcondition.tf @@ -0,0 +1,15 @@ +data "http" "example" { + url = "https://checkpoint-api.hashicorp.com/v1/check/terraform" + + # Optional request headers + request_headers = { + Accept = "application/json" + } + + lifecycle { + postcondition { + condition = contains([201, 204], self.status_code) + error_message = "Status code invalid" + } + } +} diff --git a/examples/data-sources/http/precondition.tf b/examples/data-sources/http/precondition.tf new file mode 100644 index 00000000..7c5e5040 --- /dev/null +++ b/examples/data-sources/http/precondition.tf @@ -0,0 +1,17 @@ +data "http" "example" { + url = "https://checkpoint-api.hashicorp.com/v1/check/terraform" + + # Optional request headers + request_headers = { + Accept = "application/json" + } +} + +resource "random_uuid" "example" { + lifecycle { + precondition { + condition = contains([201, 204], data.http.example.status_code) + error_message = "Status code invalid" + } + } +} diff --git a/go.mod b/go.mod index 59ae34f5..401b9080 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/terraform-providers/terraform-provider-http go 1.17 require ( - github.com/hashicorp/terraform-plugin-docs v0.10.0 + github.com/hashicorp/terraform-plugin-docs v0.10.1 github.com/hashicorp/terraform-plugin-framework v0.8.1-0.20220613174400-51e06ca1477f github.com/hashicorp/terraform-plugin-go v0.9.1 github.com/hashicorp/terraform-plugin-sdk/v2 v2.17.0 diff --git a/go.sum b/go.sum index fc24f417..e0ed0b30 100644 --- a/go.sum +++ b/go.sum @@ -149,8 +149,8 @@ github.com/hashicorp/terraform-exec v0.16.1/go.mod h1:aj0lVshy8l+MHhFNoijNHtqTJQ github.com/hashicorp/terraform-json v0.13.0/go.mod h1:y5OdLBCT+rxbwnpxZs9kGL7R9ExU76+cpdY8zHwoazk= github.com/hashicorp/terraform-json v0.14.0 h1:sh9iZ1Y8IFJLx+xQiKHGud6/TSUCM0N8e17dKDpqV7s= github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM= -github.com/hashicorp/terraform-plugin-docs v0.10.0 h1:LwoFJ3RoKElDFhBRomsDkaEn59OUNXa92M21VCuL7Vk= -github.com/hashicorp/terraform-plugin-docs v0.10.0/go.mod h1:47ZcsxMUJxAjGzHf+dZ9q78oYf4PeJxO1N+i5XDtXBc= +github.com/hashicorp/terraform-plugin-docs v0.10.1 h1:jiVYfhJ/hVXDAQN2XjLK3WH1A/YHgFCrFXPpxibvmjc= +github.com/hashicorp/terraform-plugin-docs v0.10.1/go.mod h1:47ZcsxMUJxAjGzHf+dZ9q78oYf4PeJxO1N+i5XDtXBc= github.com/hashicorp/terraform-plugin-framework v0.8.1-0.20220613174400-51e06ca1477f h1:oOvR3qwrQ98aVdjf8AfHTlHnN5af6ZyCtRXC3xrNfPg= github.com/hashicorp/terraform-plugin-framework v0.8.1-0.20220613174400-51e06ca1477f/go.mod h1:ActelD2V6yt2m0MwIX4jESGDYJ573rAvZswGjSGm1rY= github.com/hashicorp/terraform-plugin-go v0.9.1 h1:vXdHaQ6aqL+OF076nMSBV+JKPdmXlzG5mzVDD04WyPs= diff --git a/internal/provider/data_source_http.go b/internal/provider/data_source_http.go index 061c9a31..c12c3985 100644 --- a/internal/provider/data_source_http.go +++ b/internal/provider/data_source_http.go @@ -56,13 +56,19 @@ your control should be treated as untrustworthy.`, "response_headers": { Description: `A map of response header field names and values.` + - ` Duplicate headers are concatenated with according to [RFC2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2).`, + ` Duplicate headers are concatenated according to [RFC2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2).`, Type: types.MapType{ ElemType: types.StringType, }, Computed: true, }, + "status_code": { + Description: `The HTTP response status code.`, + Type: types.Int64Type, + Computed: true, + }, + "id": { Description: "The ID of this resource.", Type: types.StringType, @@ -88,6 +94,7 @@ type HTTPModel struct { RequestHeaders types.Map `tfsdk:"request_headers"` ResponseHeaders types.Map `tfsdk:"response_headers"` ResponseBody types.String `tfsdk:"response_body"` + StatusCode types.Int64 `tfsdk:"status_code"` } func (d dataSourceHTTP) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { @@ -134,14 +141,6 @@ func (d dataSourceHTTP) Read(ctx context.Context, req tfsdk.ReadDataSourceReques defer response.Body.Close() - if response.StatusCode != 200 { - resp.Diagnostics.AddError( - "HTTP response code error", - fmt.Sprintf("Response code is not 200: %d", response.StatusCode), - ) - return - } - contentType := response.Header.Get("Content-Type") if !isContentTypeText(contentType) { resp.Diagnostics.AddWarning( @@ -179,6 +178,7 @@ func (d dataSourceHTTP) Read(ctx context.Context, req tfsdk.ReadDataSourceReques model.ID = types.String{Value: url} model.ResponseHeaders = respHeadersState model.ResponseBody = types.String{Value: responseBody} + model.StatusCode = types.Int64{Value: int64(response.StatusCode)} diags = resp.State.Set(ctx, model) resp.Diagnostics.Append(diags...) diff --git a/internal/provider/data_source_http_test.go b/internal/provider/data_source_http_test.go index 4107b5de..46570637 100644 --- a/internal/provider/data_source_http_test.go +++ b/internal/provider/data_source_http_test.go @@ -4,13 +4,12 @@ import ( "fmt" "net/http" "net/http/httptest" - "regexp" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) -func TestDataSource_http200(t *testing.T) { +func TestDataSource_200(t *testing.T) { testHttpMock := setUpMockHttpServer() defer testHttpMock.server.Close() @@ -19,18 +18,23 @@ func TestDataSource_http200(t *testing.T) { ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), Steps: []resource.TestStep{ { - Config: fmt.Sprintf(testDataSourceConfigBasic, testHttpMock.server.URL, 200), + Config: fmt.Sprintf(` + data "http" "http_test" { + url = "%s/200" + }`, testHttpMock.server.URL), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("data.http.http_test", "response_body", "1.0.0"), + resource.TestCheckResourceAttr("data.http.http_test", "response_headers.Content-Type", "text/plain"), resource.TestCheckResourceAttr("data.http.http_test", "response_headers.X-Single", "foobar"), resource.TestCheckResourceAttr("data.http.http_test", "response_headers.X-Double", "1, 2"), + resource.TestCheckResourceAttr("data.http.http_test", "status_code", "200"), ), }, }, }) } -func TestDataSource_http404(t *testing.T) { +func TestDataSource_404(t *testing.T) { testHttpMock := setUpMockHttpServer() defer testHttpMock.server.Close() @@ -39,14 +43,20 @@ func TestDataSource_http404(t *testing.T) { ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), Steps: []resource.TestStep{ { - Config: fmt.Sprintf(testDataSourceConfigBasic, testHttpMock.server.URL, 404), - ExpectError: regexp.MustCompile("Response code is not 200: 404"), + Config: fmt.Sprintf(` + data "http" "http_test" { + url = "%s/404" + }`, testHttpMock.server.URL), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.http.http_test", "response_body", ""), + resource.TestCheckResourceAttr("data.http.http_test", "status_code", "404"), + ), }, }, }) } -func TestDataSource_withHeaders200(t *testing.T) { +func TestDataSource_withAuthorizationRequestHeader_200(t *testing.T) { testHttpMock := setUpMockHttpServer() defer testHttpMock.server.Close() @@ -55,16 +65,50 @@ func TestDataSource_withHeaders200(t *testing.T) { ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), Steps: []resource.TestStep{ { - Config: fmt.Sprintf(testDataSourceConfigWithHeaders, testHttpMock.server.URL, 200), + Config: fmt.Sprintf(` + data "http" "http_test" { + url = "%s/restricted" + + request_headers = { + "Authorization" = "Zm9vOmJhcg==" + } + }`, testHttpMock.server.URL), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("data.http.http_test", "response_body", "1.0.0"), + resource.TestCheckResourceAttr("data.http.http_test", "status_code", "200"), + ), + }, + }, + }) +} + +func TestDataSource_withAuthorizationRequestHeader_403(t *testing.T) { + testHttpMock := setUpMockHttpServer() + + defer testHttpMock.server.Close() + + resource.UnitTest(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` + data "http" "http_test" { + url = "%s/restricted" + + request_headers = { + "Authorization" = "unauthorized" + } + }`, testHttpMock.server.URL), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.http.http_test", "response_body", ""), + resource.TestCheckResourceAttr("data.http.http_test", "status_code", "403"), ), }, }, }) } -func TestDataSource_utf8(t *testing.T) { +func TestDataSource_utf8_200(t *testing.T) { testHttpMock := setUpMockHttpServer() defer testHttpMock.server.Close() @@ -73,16 +117,21 @@ func TestDataSource_utf8(t *testing.T) { ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), Steps: []resource.TestStep{ { - Config: fmt.Sprintf(testDataSourceConfigUTF8, testHttpMock.server.URL, 200), + Config: fmt.Sprintf(` + data "http" "http_test" { + url = "%s/utf-8/200" + }`, testHttpMock.server.URL), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("data.http.http_test", "response_body", "1.0.0"), + resource.TestCheckResourceAttr("data.http.http_test", "response_headers.Content-Type", "text/plain; charset=UTF-8"), + resource.TestCheckResourceAttr("data.http.http_test", "status_code", "200"), ), }, }, }) } -func TestDataSource_utf16(t *testing.T) { +func TestDataSource_utf16_200(t *testing.T) { testHttpMock := setUpMockHttpServer() defer testHttpMock.server.Close() @@ -91,9 +140,12 @@ func TestDataSource_utf16(t *testing.T) { ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), Steps: []resource.TestStep{ { - Config: fmt.Sprintf(testDataSourceConfigUTF16, testHttpMock.server.URL, 200), + Config: fmt.Sprintf(` + data "http" "http_test" { + url = "%s/utf-16/200" + }`, testHttpMock.server.URL), // This should now be a warning, but unsure how to test for it... - //ExpectWarning: regexp.MustCompile("Content-Type is not a text type. Got: application/json; charset=UTF-16"), + // ExpectWarning: regexp.MustCompile("Content-Type is not a text type. Got: application/json; charset=UTF-16"), }, }, }) @@ -108,49 +160,19 @@ func TestDataSource_x509cert(t *testing.T) { ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), Steps: []resource.TestStep{ { - Config: fmt.Sprintf(testDataSourceConfigx509cert, testHttpMock.server.URL), + Config: fmt.Sprintf(` + data "http" "http_test" { + url = "%s/x509-ca-cert/200" + }`, testHttpMock.server.URL), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("data.http.http_test", "response_body", "pem"), + resource.TestCheckResourceAttr("data.http.http_test", "status_code", "200"), ), }, }, }) } -const testDataSourceConfigBasic = ` -data "http" "http_test" { - url = "%s/meta_%d.txt" -} -` - -const testDataSourceConfigWithHeaders = ` -data "http" "http_test" { - url = "%s/restricted/meta_%d.txt" - - request_headers = { - "Authorization" = "Zm9vOmJhcg==" - } -} -` - -const testDataSourceConfigUTF8 = ` -data "http" "http_test" { - url = "%s/utf-8/meta_%d.txt" -} -` - -const testDataSourceConfigUTF16 = ` -data "http" "http_test" { - url = "%s/utf-16/meta_%d.txt" -} -` - -const testDataSourceConfigx509cert = ` -data "http" "http_test" { - url = "%s/x509/cert.pem" -} -` - type TestHttpMock struct { server *httptest.Server } @@ -158,36 +180,35 @@ type TestHttpMock struct { func setUpMockHttpServer() *TestHttpMock { Server := httptest.NewServer( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/plain") w.Header().Add("X-Single", "foobar") w.Header().Add("X-Double", "1") w.Header().Add("X-Double", "2") - if r.URL.Path == "/meta_200.txt" { + + switch r.URL.Path { + case "/200": w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("1.0.0")) - } else if r.URL.Path == "/restricted/meta_200.txt" { + case "/restricted": if r.Header.Get("Authorization") == "Zm9vOmJhcg==" { w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("1.0.0")) } else { w.WriteHeader(http.StatusForbidden) } - } else if r.URL.Path == "/utf-8/meta_200.txt" { + case "/utf-8/200": w.Header().Set("Content-Type", "text/plain; charset=UTF-8") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("1.0.0")) - } else if r.URL.Path == "/utf-16/meta_200.txt" { + case "/utf-16/200": w.Header().Set("Content-Type", "application/json; charset=UTF-16") w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("\"1.0.0\"")) - } else if r.URL.Path == "/x509/cert.pem" { + _, _ = w.Write([]byte("1.0.0")) + case "/x509-ca-cert/200": w.Header().Set("Content-Type", "application/x-x509-ca-cert") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("pem")) - } else if r.URL.Path == "/meta_404.txt" { - w.WriteHeader(http.StatusNotFound) - } else { + default: w.WriteHeader(http.StatusNotFound) } }), diff --git a/internal/provider/provider.go b/internal/provider/provider.go index e6c60ba0..c0c586a8 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -25,7 +25,7 @@ func (p *provider) GetResources(_ context.Context) (map[string]tfsdk.ResourceTyp return map[string]tfsdk.ResourceType{}, nil } -func (p *provider) GetDataSources(ctx context.Context) (map[string]tfsdk.DataSourceType, diag.Diagnostics) { +func (p *provider) GetDataSources(_ context.Context) (map[string]tfsdk.DataSourceType, diag.Diagnostics) { return map[string]tfsdk.DataSourceType{ "http": dataSourceHTTPType{}, }, nil diff --git a/templates/data-sources/http.md.tmpl b/templates/data-sources/http.md.tmpl new file mode 100644 index 00000000..418b1048 --- /dev/null +++ b/templates/data-sources/http.md.tmpl @@ -0,0 +1,30 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/data-sources/http/data-source.tf" }} + +## Usage with Postcondition + +[Precondition and Postcondition](https://www.terraform.io/language/expressions/custom-conditions) +checks are available with Terraform v1.2.0 and later. + +{{ tffile "examples/data-sources/http/postcondition.tf" }} + +## Usage with Precondition + +[Precondition and Postcondition](https://www.terraform.io/language/expressions/custom-conditions) +checks are available with Terraform v1.2.0 and later. + +{{ tffile "examples/data-sources/http/precondition.tf" }} + +{{ .SchemaMarkdown | trimspace }} From abaab6e0345409f71917507f719ec01b844be5ef Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Wed, 15 Jun 2022 09:18:14 +0100 Subject: [PATCH 05/10] Compile time checks for interface implementation (#93) --- internal/provider/data_source_http.go | 12 ++++++++---- internal/provider/provider.go | 4 +++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/internal/provider/data_source_http.go b/internal/provider/data_source_http.go index c12c3985..f39de053 100644 --- a/internal/provider/data_source_http.go +++ b/internal/provider/data_source_http.go @@ -17,7 +17,9 @@ import ( type dataSourceHTTPType struct { } -func (d dataSourceHTTPType) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { +var _ tfsdk.DataSourceType = (*dataSourceHTTPType)(nil) + +func (d *dataSourceHTTPType) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{ Description: ` The ` + "`http`" + ` data source makes an HTTP GET request to the given URL and exports @@ -78,8 +80,8 @@ your control should be treated as untrustworthy.`, }, nil } -func (d dataSourceHTTPType) NewDataSource(ctx context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { - return dataSourceHTTP{ +func (d *dataSourceHTTPType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { + return &dataSourceHTTP{ p: *(p.(*provider)), }, nil } @@ -88,6 +90,8 @@ type dataSourceHTTP struct { p provider } +var _ tfsdk.DataSource = (*dataSourceHTTP)(nil) + type HTTPModel struct { ID types.String `tfsdk:"id"` URL types.String `tfsdk:"url"` @@ -97,7 +101,7 @@ type HTTPModel struct { StatusCode types.Int64 `tfsdk:"status_code"` } -func (d dataSourceHTTP) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { +func (d *dataSourceHTTP) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { var model HTTPModel diags := req.Config.Get(ctx, &model) resp.Diagnostics.Append(diags...) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index c0c586a8..b93b1a63 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -10,6 +10,8 @@ import ( type provider struct { } +var _ tfsdk.Provider = (*provider)(nil) + func New() tfsdk.Provider { return &provider{} } @@ -27,6 +29,6 @@ func (p *provider) GetResources(_ context.Context) (map[string]tfsdk.ResourceTyp func (p *provider) GetDataSources(_ context.Context) (map[string]tfsdk.DataSourceType, diag.Diagnostics) { return map[string]tfsdk.DataSourceType{ - "http": dataSourceHTTPType{}, + "http": &dataSourceHTTPType{}, }, nil } From 4f01898399976c0ea78adea73c23a21e175ab056 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 28 Jun 2022 14:06:51 +0100 Subject: [PATCH 06/10] Refactoring layout (#93) --- CHANGELOG.md | 9 ++- .../http}/data_source_http.go | 45 ++++++------ internal/provider/provider.go | 19 ++--- ...t.go => provider_data_source_http_test.go} | 71 +++++++++++++++---- internal/provider/provider_test.go | 2 +- terraform-registry-manifest.json | 2 +- 6 files changed, 98 insertions(+), 50 deletions(-) rename internal/{provider => datasources/http}/data_source_http.go (89%) rename internal/provider/{data_source_http_test.go => provider_data_source_http_test.go} (72%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4549164d..9736ad06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,16 @@ ## 3.0.0 (unreleased) +NOTES: + +* Provider has been re-written using the new [`terraform-plugin-framework`](https://www.terraform.io/plugin/framework) ([#177](https://github.com/hashicorp/terraform-provider-http/pull/142)). + BREAKING CHANGES: -* data-source/http: Provider has been migrated from SDKv2 to Framework ([#142](https://github.com/hashicorp/terraform-provider-http/pull/142)). +* [Terraform `>=1.0`](https://www.terraform.io/language/upgrade-guides/1-0) is now required to use this provider. + * data-source/http: There is no longer a check that the status code is 200 following a request. `status_code` attribute has been added and should be used in [precondition and postcondition](https://www.terraform.io/language/expressions/custom-conditions) checks instead ([114](https://github.com/hashicorp/terraform-provider-http/pull/114)). -* data-source/http: `body` has been removed ([#137](https://github.com/hashicorp/terraform-provider-http/pull/137)). +* data-source/http: Deprecated `body` has been removed ([#137](https://github.com/hashicorp/terraform-provider-http/pull/137)). ## 2.2.0 (June 02, 2022) diff --git a/internal/provider/data_source_http.go b/internal/datasources/http/data_source_http.go similarity index 89% rename from internal/provider/data_source_http.go rename to internal/datasources/http/data_source_http.go index f39de053..3e6d1fb2 100644 --- a/internal/provider/data_source_http.go +++ b/internal/datasources/http/data_source_http.go @@ -1,4 +1,4 @@ -package provider +package http import ( "context" @@ -14,12 +14,15 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) -type dataSourceHTTPType struct { +func NewDataSourceType() *dataSourceType { + return &dataSourceType{} } -var _ tfsdk.DataSourceType = (*dataSourceHTTPType)(nil) +var _ tfsdk.DataSourceType = (*dataSourceType)(nil) -func (d *dataSourceHTTPType) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { +type dataSourceType struct{} + +func (d *dataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{ Description: ` The ` + "`http`" + ` data source makes an HTTP GET request to the given URL and exports @@ -80,29 +83,16 @@ your control should be treated as untrustworthy.`, }, nil } -func (d *dataSourceHTTPType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { - return &dataSourceHTTP{ - p: *(p.(*provider)), - }, nil +func (d *dataSourceType) NewDataSource(context.Context, tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { + return &dataSource{}, nil } -type dataSourceHTTP struct { - p provider -} +var _ tfsdk.DataSource = (*dataSource)(nil) -var _ tfsdk.DataSource = (*dataSourceHTTP)(nil) +type dataSource struct{} -type HTTPModel struct { - ID types.String `tfsdk:"id"` - URL types.String `tfsdk:"url"` - RequestHeaders types.Map `tfsdk:"request_headers"` - ResponseHeaders types.Map `tfsdk:"response_headers"` - ResponseBody types.String `tfsdk:"response_body"` - StatusCode types.Int64 `tfsdk:"status_code"` -} - -func (d *dataSourceHTTP) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { - var model HTTPModel +func (d *dataSource) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { + var model modelV0 diags := req.Config.Get(ctx, &model) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -213,3 +203,12 @@ func isContentTypeText(contentType string) bool { return false } + +type modelV0 struct { + ID types.String `tfsdk:"id"` + URL types.String `tfsdk:"url"` + RequestHeaders types.Map `tfsdk:"request_headers"` + ResponseHeaders types.Map `tfsdk:"response_headers"` + ResponseBody types.String `tfsdk:"response_body"` + StatusCode types.Int64 `tfsdk:"status_code"` +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index b93b1a63..b77d5e54 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -5,30 +5,31 @@ import ( "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" + + httprequest "github.com/terraform-providers/terraform-provider-http/internal/datasources/http" ) -type provider struct { +func New() tfsdk.Provider { + return &provider{} } var _ tfsdk.Provider = (*provider)(nil) -func New() tfsdk.Provider { - return &provider{} -} +type provider struct{} -func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { +func (p *provider) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{}, nil } -func (p *provider) Configure(_ context.Context, _ tfsdk.ConfigureProviderRequest, _ *tfsdk.ConfigureProviderResponse) { +func (p *provider) Configure(context.Context, tfsdk.ConfigureProviderRequest, *tfsdk.ConfigureProviderResponse) { } -func (p *provider) GetResources(_ context.Context) (map[string]tfsdk.ResourceType, diag.Diagnostics) { +func (p *provider) GetResources(context.Context) (map[string]tfsdk.ResourceType, diag.Diagnostics) { return map[string]tfsdk.ResourceType{}, nil } -func (p *provider) GetDataSources(_ context.Context) (map[string]tfsdk.DataSourceType, diag.Diagnostics) { +func (p *provider) GetDataSources(context.Context) (map[string]tfsdk.DataSourceType, diag.Diagnostics) { return map[string]tfsdk.DataSourceType{ - "http": &dataSourceHTTPType{}, + "http": httprequest.NewDataSourceType(), }, nil } diff --git a/internal/provider/data_source_http_test.go b/internal/provider/provider_data_source_http_test.go similarity index 72% rename from internal/provider/data_source_http_test.go rename to internal/provider/provider_data_source_http_test.go index 46570637..3dceaa97 100644 --- a/internal/provider/data_source_http_test.go +++ b/internal/provider/provider_data_source_http_test.go @@ -11,11 +11,10 @@ import ( func TestDataSource_200(t *testing.T) { testHttpMock := setUpMockHttpServer() - defer testHttpMock.server.Close() resource.UnitTest(t, resource.TestCase{ - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), + ProtoV6ProviderFactories: protoV6ProviderFactories(), Steps: []resource.TestStep{ { Config: fmt.Sprintf(` @@ -36,11 +35,10 @@ func TestDataSource_200(t *testing.T) { func TestDataSource_404(t *testing.T) { testHttpMock := setUpMockHttpServer() - defer testHttpMock.server.Close() resource.UnitTest(t, resource.TestCase{ - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), + ProtoV6ProviderFactories: protoV6ProviderFactories(), Steps: []resource.TestStep{ { Config: fmt.Sprintf(` @@ -58,11 +56,10 @@ func TestDataSource_404(t *testing.T) { func TestDataSource_withAuthorizationRequestHeader_200(t *testing.T) { testHttpMock := setUpMockHttpServer() - defer testHttpMock.server.Close() resource.UnitTest(t, resource.TestCase{ - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), + ProtoV6ProviderFactories: protoV6ProviderFactories(), Steps: []resource.TestStep{ { Config: fmt.Sprintf(` @@ -84,11 +81,10 @@ func TestDataSource_withAuthorizationRequestHeader_200(t *testing.T) { func TestDataSource_withAuthorizationRequestHeader_403(t *testing.T) { testHttpMock := setUpMockHttpServer() - defer testHttpMock.server.Close() resource.UnitTest(t, resource.TestCase{ - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), + ProtoV6ProviderFactories: protoV6ProviderFactories(), Steps: []resource.TestStep{ { Config: fmt.Sprintf(` @@ -110,11 +106,10 @@ func TestDataSource_withAuthorizationRequestHeader_403(t *testing.T) { func TestDataSource_utf8_200(t *testing.T) { testHttpMock := setUpMockHttpServer() - defer testHttpMock.server.Close() resource.UnitTest(t, resource.TestCase{ - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), + ProtoV6ProviderFactories: protoV6ProviderFactories(), Steps: []resource.TestStep{ { Config: fmt.Sprintf(` @@ -133,11 +128,10 @@ func TestDataSource_utf8_200(t *testing.T) { func TestDataSource_utf16_200(t *testing.T) { testHttpMock := setUpMockHttpServer() - defer testHttpMock.server.Close() resource.UnitTest(t, resource.TestCase{ - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), + ProtoV6ProviderFactories: protoV6ProviderFactories(), Steps: []resource.TestStep{ { Config: fmt.Sprintf(` @@ -153,11 +147,10 @@ func TestDataSource_utf16_200(t *testing.T) { func TestDataSource_x509cert(t *testing.T) { testHttpMock := setUpMockHttpServer() - defer testHttpMock.server.Close() resource.UnitTest(t, resource.TestCase{ - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(), + ProtoV6ProviderFactories: protoV6ProviderFactories(), Steps: []resource.TestStep{ { Config: fmt.Sprintf(` @@ -173,6 +166,56 @@ func TestDataSource_x509cert(t *testing.T) { }) } +func TestDataSource_UpgradeFromVersion2_2_0(t *testing.T) { + testHttpMock := setUpMockHttpServer() + defer testHttpMock.server.Close() + + resource.Test(t, resource.TestCase{ + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "http": { + VersionConstraint: "2.2.0", + Source: "hashicorp/http", + }, + }, + Config: fmt.Sprintf(` + data "http" "http_test" { + url = "%s/200" + }`, testHttpMock.server.URL), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.http.http_test", "response_body", "1.0.0"), + resource.TestCheckResourceAttr("data.http.http_test", "response_headers.Content-Type", "text/plain"), + resource.TestCheckResourceAttr("data.http.http_test", "response_headers.X-Single", "foobar"), + resource.TestCheckResourceAttr("data.http.http_test", "response_headers.X-Double", "1, 2"), + ), + }, + { + ProtoV6ProviderFactories: protoV6ProviderFactories(), + Config: fmt.Sprintf(` + data "http" "http_test" { + url = "%s/200" + }`, testHttpMock.server.URL), + PlanOnly: true, + }, + { + ProtoV6ProviderFactories: protoV6ProviderFactories(), + Config: fmt.Sprintf(` + data "http" "http_test" { + url = "%s/200" + }`, testHttpMock.server.URL), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.http.http_test", "response_body", "1.0.0"), + resource.TestCheckResourceAttr("data.http.http_test", "response_headers.Content-Type", "text/plain"), + resource.TestCheckResourceAttr("data.http.http_test", "response_headers.X-Single", "foobar"), + resource.TestCheckResourceAttr("data.http.http_test", "response_headers.X-Double", "1, 2"), + resource.TestCheckResourceAttr("data.http.http_test", "status_code", "200"), + ), + }, + }, + }) +} + type TestHttpMock struct { server *httptest.Server } diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index 05d4400b..8ecccac0 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -6,7 +6,7 @@ import ( ) //nolint:unparam -func testAccProtoV6ProviderFactories() map[string]func() (tfprotov6.ProviderServer, error) { +func protoV6ProviderFactories() map[string]func() (tfprotov6.ProviderServer, error) { return map[string]func() (tfprotov6.ProviderServer, error){ "http": func() (tfprotov6.ProviderServer, error) { return providerserver.NewProtocol6(New())(), nil diff --git a/terraform-registry-manifest.json b/terraform-registry-manifest.json index a8286e38..6e86c621 100644 --- a/terraform-registry-manifest.json +++ b/terraform-registry-manifest.json @@ -1,6 +1,6 @@ { "version": 1, "metadata": { - "protocol_versions": ["5.0"] + "protocol_versions": ["6.0"] } } \ No newline at end of file From 58ca95cac11d2ce4e3484a3d73d8cc837f46540e Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 28 Jun 2022 14:12:30 +0100 Subject: [PATCH 07/10] Adding support for debug flag (#93) --- main.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/main.go b/main.go index 8ab18824..2e238617 100644 --- a/main.go +++ b/main.go @@ -2,9 +2,11 @@ package main import ( "context" + "flag" "log" "github.com/hashicorp/terraform-plugin-framework/providerserver" + "github.com/terraform-providers/terraform-provider-http/internal/provider" ) @@ -19,8 +21,14 @@ import ( //go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs func main() { + var debug bool + + flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") + flag.Parse() + err := providerserver.Serve(context.Background(), provider.New, providerserver.ServeOpts{ Address: "registry.terraform.io/hashicorp/http", + Debug: debug, }) if err != nil { log.Fatal(err) From fb278fbe106338407766c727317568dc234df602 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 30 Jun 2022 10:56:21 +0100 Subject: [PATCH 08/10] Refactoring location of data source and using NewProtocol6WithError for tests (#93) --- .../http => provider}/data_source_http.go | 22 ++++++++----------- ..._http_test.go => data_source_http_test.go} | 0 internal/provider/provider.go | 4 +--- internal/provider/provider_test.go | 2 +- 4 files changed, 11 insertions(+), 17 deletions(-) rename internal/{datasources/http => provider}/data_source_http.go (90%) rename internal/provider/{provider_data_source_http_test.go => data_source_http_test.go} (100%) diff --git a/internal/datasources/http/data_source_http.go b/internal/provider/data_source_http.go similarity index 90% rename from internal/datasources/http/data_source_http.go rename to internal/provider/data_source_http.go index 3e6d1fb2..a6a42a92 100644 --- a/internal/datasources/http/data_source_http.go +++ b/internal/provider/data_source_http.go @@ -1,4 +1,4 @@ -package http +package provider import ( "context" @@ -14,15 +14,11 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) -func NewDataSourceType() *dataSourceType { - return &dataSourceType{} -} - -var _ tfsdk.DataSourceType = (*dataSourceType)(nil) +var _ tfsdk.DataSourceType = (*httpDataSourceType)(nil) -type dataSourceType struct{} +type httpDataSourceType struct{} -func (d *dataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { +func (d *httpDataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{ Description: ` The ` + "`http`" + ` data source makes an HTTP GET request to the given URL and exports @@ -83,15 +79,15 @@ your control should be treated as untrustworthy.`, }, nil } -func (d *dataSourceType) NewDataSource(context.Context, tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { - return &dataSource{}, nil +func (d *httpDataSourceType) NewDataSource(context.Context, tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { + return &httpDataSource{}, nil } -var _ tfsdk.DataSource = (*dataSource)(nil) +var _ tfsdk.DataSource = (*httpDataSource)(nil) -type dataSource struct{} +type httpDataSource struct{} -func (d *dataSource) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { +func (d *httpDataSource) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { var model modelV0 diags := req.Config.Get(ctx, &model) resp.Diagnostics.Append(diags...) diff --git a/internal/provider/provider_data_source_http_test.go b/internal/provider/data_source_http_test.go similarity index 100% rename from internal/provider/provider_data_source_http_test.go rename to internal/provider/data_source_http_test.go diff --git a/internal/provider/provider.go b/internal/provider/provider.go index b77d5e54..8e894d3a 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -5,8 +5,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - - httprequest "github.com/terraform-providers/terraform-provider-http/internal/datasources/http" ) func New() tfsdk.Provider { @@ -30,6 +28,6 @@ func (p *provider) GetResources(context.Context) (map[string]tfsdk.ResourceType, func (p *provider) GetDataSources(context.Context) (map[string]tfsdk.DataSourceType, diag.Diagnostics) { return map[string]tfsdk.DataSourceType{ - "http": httprequest.NewDataSourceType(), + "http": &httpDataSourceType{}, }, nil } diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index 8ecccac0..c9846159 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -9,7 +9,7 @@ import ( func protoV6ProviderFactories() map[string]func() (tfprotov6.ProviderServer, error) { return map[string]func() (tfprotov6.ProviderServer, error){ "http": func() (tfprotov6.ProviderServer, error) { - return providerserver.NewProtocol6(New())(), nil + return providerserver.NewProtocol6WithError(New())() }, } } From 92db4b43be88f8b7a0c0b3e8381e609fcf0a2246 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Fri, 1 Jul 2022 17:15:09 +0100 Subject: [PATCH 09/10] Using NewProtocol6WithError for tests (#93) --- internal/provider/provider_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index c9846159..361402a5 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -8,8 +8,6 @@ import ( //nolint:unparam func protoV6ProviderFactories() map[string]func() (tfprotov6.ProviderServer, error) { return map[string]func() (tfprotov6.ProviderServer, error){ - "http": func() (tfprotov6.ProviderServer, error) { - return providerserver.NewProtocol6WithError(New())() - }, + "http": providerserver.NewProtocol6WithError(New()), } } From e615243cc23f5c02ea67006b4aa28809ac925c8e Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Fri, 1 Jul 2022 17:17:58 +0100 Subject: [PATCH 10/10] go mod tidy (#93) --- go.sum | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/go.sum b/go.sum index f7bfa632..fe9f34bd 100644 --- a/go.sum +++ b/go.sum @@ -152,15 +152,10 @@ github.com/hashicorp/terraform-exec v0.17.1/go.mod h1:0J3KZWwroWCS+ObFZMEUSMD9Gz github.com/hashicorp/terraform-json v0.13.0/go.mod h1:y5OdLBCT+rxbwnpxZs9kGL7R9ExU76+cpdY8zHwoazk= github.com/hashicorp/terraform-json v0.14.0 h1:sh9iZ1Y8IFJLx+xQiKHGud6/TSUCM0N8e17dKDpqV7s= github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM= -<<<<<<< HEAD -github.com/hashicorp/terraform-plugin-docs v0.11.0 h1:CO7AetK71PjeLHlavvgClzncWtrAZo+rST5M3yNX0UM= -github.com/hashicorp/terraform-plugin-docs v0.11.0/go.mod h1:HVn60yjtl4XxLINPgNmPCwX8SQ4T99Ut9CTD/ac6i5w= -github.com/hashicorp/terraform-plugin-framework v0.9.0 h1:vOKG9+keJv062zGhXFgfOFEuGcfgV6LHciwleFTSek0= -github.com/hashicorp/terraform-plugin-framework v0.9.0/go.mod h1:ActelD2V6yt2m0MwIX4jESGDYJ573rAvZswGjSGm1rY= -======= github.com/hashicorp/terraform-plugin-docs v0.12.0 h1:EAvFVEoV/wj15t/VSeKVpnAd+BBnIxzYepAnScBWrU4= github.com/hashicorp/terraform-plugin-docs v0.12.0/go.mod h1:HVn60yjtl4XxLINPgNmPCwX8SQ4T99Ut9CTD/ac6i5w= ->>>>>>> origin/main +github.com/hashicorp/terraform-plugin-framework v0.9.0 h1:vOKG9+keJv062zGhXFgfOFEuGcfgV6LHciwleFTSek0= +github.com/hashicorp/terraform-plugin-framework v0.9.0/go.mod h1:ActelD2V6yt2m0MwIX4jESGDYJ573rAvZswGjSGm1rY= github.com/hashicorp/terraform-plugin-go v0.9.1 h1:vXdHaQ6aqL+OF076nMSBV+JKPdmXlzG5mzVDD04WyPs= github.com/hashicorp/terraform-plugin-go v0.9.1/go.mod h1:ItjVSlQs70otlzcCwlPcU8FRXLdO973oYFRZwAOxy8M= github.com/hashicorp/terraform-plugin-log v0.4.0/go.mod h1:9KclxdunFownr4pIm1jdmwKRmE4d6HVG2c9XDq47rpg=