forked from IBM-Cloud/terraform
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
http provider and http request data source
- Loading branch information
1 parent
9a1c6d9
commit ace0456
Showing
9 changed files
with
400 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package http | ||
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"regexp" | ||
"time" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
func dataSource() *schema.Resource { | ||
return &schema.Resource{ | ||
Read: dataSourceRead, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"url": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
Elem: &schema.Schema{ | ||
Type: schema.TypeString, | ||
}, | ||
}, | ||
|
||
"request_headers": &schema.Schema{ | ||
Type: schema.TypeMap, | ||
Optional: true, | ||
Elem: &schema.Schema{ | ||
Type: schema.TypeString, | ||
}, | ||
}, | ||
|
||
"body": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Computed: true, | ||
Elem: &schema.Schema{ | ||
Type: schema.TypeString, | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func dataSourceRead(d *schema.ResourceData, meta interface{}) error { | ||
|
||
url := d.Get("url").(string) | ||
headers := d.Get("request_headers").(map[string]interface{}) | ||
|
||
client := &http.Client{} | ||
|
||
req, err := http.NewRequest("GET", url, nil) | ||
if err != nil { | ||
return fmt.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 fmt.Errorf("Error during making a request: %s", url) | ||
} | ||
|
||
defer resp.Body.Close() | ||
|
||
if resp.StatusCode != 200 { | ||
return fmt.Errorf("HTTP request error. Response code: %d", resp.StatusCode) | ||
} | ||
|
||
contentType := resp.Header.Get("Content-Type") | ||
if contentType == "" || isContentTypeAllowed(contentType) == false { | ||
return fmt.Errorf("Content-Type is not a text type. Got: %s", contentType) | ||
} | ||
|
||
bytes, err := ioutil.ReadAll(resp.Body) | ||
if err != nil { | ||
return fmt.Errorf("Error while reading response body. %s", err) | ||
} | ||
|
||
d.Set("body", string(bytes)) | ||
d.SetId(time.Now().UTC().String()) | ||
|
||
return nil | ||
} | ||
|
||
// This is to prevent potential issues w/ binary files | ||
// and generally unprintable characters | ||
// See https://github.com/hashicorp/terraform/pull/3858#issuecomment-156856738 | ||
func isContentTypeAllowed(contentType string) bool { | ||
allowedContentTypes := []*regexp.Regexp{ | ||
regexp.MustCompile("^text/.+"), | ||
regexp.MustCompile("^application/json$"), | ||
} | ||
|
||
for _, r := range allowedContentTypes { | ||
if r.MatchString(contentType) { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package http | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"net/http/httptest" | ||
"regexp" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
type TestHttpMock struct { | ||
server *httptest.Server | ||
} | ||
|
||
const testDataSourceConfig_basic = ` | ||
data "http" "http_test" { | ||
url = "%s/meta_%d.txt" | ||
} | ||
output "body" { | ||
value = "${data.http.http_test.body}" | ||
} | ||
` | ||
|
||
func TestDataSource_http200(t *testing.T) { | ||
testHttpMock := setUpMockHttpServer() | ||
|
||
defer testHttpMock.server.Close() | ||
|
||
resource.UnitTest(t, resource.TestCase{ | ||
Providers: testProviders, | ||
Steps: []resource.TestStep{ | ||
resource.TestStep{ | ||
Config: fmt.Sprintf(testDataSourceConfig_basic, testHttpMock.server.URL, 200), | ||
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 != "1.0.0" { | ||
return fmt.Errorf( | ||
`'body' output is %s; want '1.0.0'`, | ||
outputs["body"].Value, | ||
) | ||
} | ||
|
||
return nil | ||
}, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestDataSource_http404(t *testing.T) { | ||
testHttpMock := setUpMockHttpServer() | ||
|
||
defer testHttpMock.server.Close() | ||
|
||
resource.UnitTest(t, resource.TestCase{ | ||
Providers: testProviders, | ||
Steps: []resource.TestStep{ | ||
resource.TestStep{ | ||
Config: fmt.Sprintf(testDataSourceConfig_basic, testHttpMock.server.URL, 404), | ||
ExpectError: regexp.MustCompile("HTTP request error. Response code: 404"), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
const testDataSourceConfig_withHeaders = ` | ||
data "http" "http_test" { | ||
url = "%s/restricted/meta_%d.txt" | ||
request_headers = { | ||
"Authorization" = "Zm9vOmJhcg==" | ||
} | ||
} | ||
output "body" { | ||
value = "${data.http.http_test.body}" | ||
} | ||
` | ||
|
||
func TestDataSource_withHeaders200(t *testing.T) { | ||
testHttpMock := setUpMockHttpServer() | ||
|
||
defer testHttpMock.server.Close() | ||
|
||
resource.UnitTest(t, resource.TestCase{ | ||
Providers: testProviders, | ||
Steps: []resource.TestStep{ | ||
resource.TestStep{ | ||
Config: fmt.Sprintf(testDataSourceConfig_withHeaders, testHttpMock.server.URL, 200), | ||
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 != "1.0.0" { | ||
return fmt.Errorf( | ||
`'body' output is %s; want '1.0.0'`, | ||
outputs["body"].Value, | ||
) | ||
} | ||
|
||
return nil | ||
}, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
const testDataSourceConfig_error = ` | ||
data "http" "http_test" { | ||
} | ||
` | ||
|
||
func TestDataSource_compileError(t *testing.T) { | ||
resource.UnitTest(t, resource.TestCase{ | ||
Providers: testProviders, | ||
Steps: []resource.TestStep{ | ||
resource.TestStep{ | ||
Config: testDataSourceConfig_error, | ||
ExpectError: regexp.MustCompile("required field is not set"), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func setUpMockHttpServer() *TestHttpMock { | ||
Server := httptest.NewServer( | ||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
if r.URL.Path == "/meta_200.txt" { | ||
w.WriteHeader(http.StatusOK) | ||
w.Write([]byte("1.0.0")) | ||
} else if r.URL.Path == "/restricted/meta_200.txt" { | ||
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 == "/meta_404.txt" { | ||
w.WriteHeader(http.StatusNotFound) | ||
} else { | ||
w.WriteHeader(http.StatusNotFound) | ||
} | ||
|
||
w.Header().Add("Content-Type", "text/plain") | ||
}), | ||
) | ||
|
||
return &TestHttpMock{ | ||
server: Server, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package http | ||
|
||
import ( | ||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
func Provider() terraform.ResourceProvider { | ||
return &schema.Provider{ | ||
Schema: map[string]*schema.Schema{}, | ||
|
||
DataSourcesMap: map[string]*schema.Resource{ | ||
"http": dataSource(), | ||
}, | ||
|
||
ResourcesMap: map[string]*schema.Resource{}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package http | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
var testProviders = map[string]terraform.ResourceProvider{ | ||
"http": Provider(), | ||
} | ||
|
||
func TestProvider(t *testing.T) { | ||
if err := Provider().(*schema.Provider).InternalValidate(); err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
--- | ||
layout: "http" | ||
page_title: "HTTP Data Source" | ||
sidebar_current: "docs-http-data-source" | ||
description: |- | ||
Retrieves the content at an HTTP or HTTPS URL. | ||
--- | ||
|
||
# `http` Data Source | ||
|
||
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. | ||
|
||
## Example Usage | ||
|
||
```hcl | ||
data "http" "example" { | ||
url = "https://checkpoint-api.hashicorp.com/v1/check/terraform" | ||
# Optional request headers | ||
request_headers { | ||
"Accept" = "application/json" | ||
} | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
* `url` - (Required) The URL to request data from. This URL must respond with | ||
a `200 OK` response and a `text/*` or `application/json` Content-Type. | ||
|
||
* `request_headers` - (Optional) A map of strings representing additional HTTP | ||
headers to include in the request. | ||
|
||
## Attributes Reference | ||
|
||
The following attributes are exported: | ||
|
||
* `body` - The raw body of the HTTP response. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
--- | ||
layout: "http" | ||
page_title: "Provider: HTTP" | ||
sidebar_current: "docs-http-index" | ||
description: |- | ||
The HTTP provider interacts with HTTP servers. | ||
--- | ||
|
||
# HTTP Provider | ||
|
||
The HTTP provider is a utility provider for interacting with generic HTTP | ||
servers as part of a Terraform configuration. | ||
|
||
This provider requires no configuration. For information on the resources | ||
it provides, see the navigation bar. |
Oops, something went wrong.