Skip to content

Commit

Permalink
Add request_body (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
masterfuzz authored Mar 28, 2023
1 parent 221b9e5 commit a909dca
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 12 deletions.
1 change: 1 addition & 0 deletions docs/resources/http_health.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ resource "checkmate_http_health" "example_insecure_tls" {
- `insecure_tls` (Boolean) Wether or not to completely skip the TLS CA verification. Default false.
- `interval` (Number) Interval in milliseconds between attemps. Default 200
- `method` (String) HTTP Method, defaults to GET
- `request_body` (String) Optional request body to send on each attempt.
- `request_timeout` (Number) Timeout for an individual request. If exceeded, the attempt will be considered failure and potentially retried. Default 1000
- `status_code` (String) Status Code to expect. Default 200
- `timeout` (Number) Overall timeout in milliseconds for the check before giving up. Default 5000
Expand Down
6 changes: 4 additions & 2 deletions internal/helpers/retry.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ const (
TimeoutExceeded
)

func (r *RetryWindow) Do(action func(successes int) bool) RetryResult {
func (r *RetryWindow) Do(action func(attempt int, successes int) bool) RetryResult {
success := make(chan bool)
go func() {
attempt := 0
successCount := 0
// run a while true loop, exiting when the timeout expires
for {
if action(successCount) {
attempt++
if action(attempt, successCount) {
successCount++
if successCount >= r.ConsecutiveSuccesses {
success <- true
Expand Down
15 changes: 12 additions & 3 deletions internal/provider/resource_http_health.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"net/http"
"net/url"
"strconv"
"strings"
"time"

"github.com/google/uuid"
Expand Down Expand Up @@ -105,6 +106,10 @@ func (*HttpHealthResource) Schema(ctx context.Context, req resource.SchemaReques
MarkdownDescription: "Identifier",
PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()},
},
"request_body": schema.StringAttribute{
MarkdownDescription: "Optional request body to send on each attempt.",
Optional: true,
},
"result_body": schema.StringAttribute{
Computed: true,
MarkdownDescription: "Result body",
Expand Down Expand Up @@ -141,6 +146,7 @@ type HttpHealthResourceModel struct {
Headers types.Map `tfsdk:"headers"`
IgnoreFailure types.Bool `tfsdk:"create_anyway_on_check_failure"`
Passed types.Bool `tfsdk:"passed"`
RequestBody types.String `tfsdk:"request_body"`
ResultBody types.String `tfsdk:"result_body"`
CABundle types.String `tfsdk:"ca_bundle"`
InsecureTLS types.Bool `tfsdk:"insecure_tls"`
Expand Down Expand Up @@ -197,8 +203,8 @@ func (r *HttpHealthResource) HealthCheck(ctx context.Context, data *HttpHealthRe
return
}

for k, v := range data.Headers.Elements() {
headers[k] = []string{v.String()}
for k, v := range tmp {
headers[k] = []string{v}
}
}

Expand Down Expand Up @@ -234,15 +240,18 @@ func (r *HttpHealthResource) HealthCheck(ctx context.Context, data *HttpHealthRe
tflog.Debug(ctx, fmt.Sprintf("%s: %s", h, v))
}

result := window.Do(func(successes int) bool {
result := window.Do(func(attempt int, successes int) bool {
if successes != 0 {
tflog.Trace(ctx, fmt.Sprintf("SUCCESS [%d/%d] http %s %s", successes, data.ConsecutiveSuccesses.ValueInt64(), data.Method.ValueString(), endpoint))
} else {
tflog.Trace(ctx, fmt.Sprintf("ATTEMPT #%d http %s %s", attempt, data.Method.ValueString(), endpoint))
}

httpResponse, err := client.Do(&http.Request{
URL: endpoint,
Method: data.Method.ValueString(),
Header: headers,
Body: io.NopCloser(strings.NewReader(data.RequestBody.ValueString())),
})
if err != nil {
diag.AddWarning("Error connecting to healthcheck endpoint", fmt.Sprintf("%s", err))
Expand Down
71 changes: 65 additions & 6 deletions internal/provider/resource_http_health_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package provider

import (
"encoding/json"
"fmt"
"testing"

Expand All @@ -31,11 +32,23 @@ func TestAccHttpHealthResource(t *testing.T) {
Steps: []resource.TestStep{
// Create and Read testing
{
Config: testAccHttpHealthResourceConfig(testUrl),
Config: testAccHttpHealthResourceConfig("test", testUrl),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("checkmate_http_health.test", "url", testUrl),
),
},
{
Config: testAccHttpHealthResourceConfig("test_headers", "https://httpbin.org/headers"),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrWith("checkmate_http_health.test_headers", "result_body", checkHeader("Hello", "world")),
),
},
{
Config: testAccHttpHealthResourceConfigWithBody("test_post", "https://httpbin.org/post", "hello"),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrWith("checkmate_http_health.test_post", "result_body", checkResponse("hello")),
),
},
// ImportState testing
// {
// ResourceName: "checkmate_http_health.test",
Expand All @@ -54,14 +67,60 @@ func TestAccHttpHealthResource(t *testing.T) {
})
}

func testAccHttpHealthResourceConfig(url string) string {
func testAccHttpHealthResourceConfig(name string, url string) string {
return fmt.Sprintf(`
resource "checkmate_http_health" "test" {
url = %[1]q
consecutive_successes = 5
resource "checkmate_http_health" %[1]q {
url = %[2]q
consecutive_successes = 1
headers = {
hello = "world"
}
}
`, url)
`, name, url)
}
func testAccHttpHealthResourceConfigWithBody(name string, url string, body string) string {
return fmt.Sprintf(`
resource "checkmate_http_health" %[1]q {
url = %[2]q
consecutive_successes = 1
method = "POST"
headers = {
"Content-Type" = "application/text"
}
request_body = %[3]q
}
`, name, url, body)

}

func checkHeader(key string, value string) func(string) error {
return func(responseBody string) error {
var parsed map[string]map[string]string
if err := json.Unmarshal([]byte(responseBody), &parsed); err != nil {
return err
}
if val, ok := parsed["headers"][key]; ok {
if val == value {
return nil
}
return fmt.Errorf("Key %q exists but value %q does not match", key, val)
}
return fmt.Errorf("Key %q does not exist in returned headers", key)
}
}

func checkResponse(value string) func(string) error {
return func(responseBody string) error {
var parsed map[string]interface{}
if err := json.Unmarshal([]byte(responseBody), &parsed); err != nil {
return err
}
if val, ok := parsed["data"]; ok {
if val == value {
return nil
}
return fmt.Errorf("Value returned %q does not match %q", parsed["data"], val)
}
return fmt.Errorf("Bad response from httpbin")
}
}
2 changes: 1 addition & 1 deletion internal/provider/resource_local_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func (r *LocalCommandResource) RunCommand(ctx context.Context, data *LocalComman
ConsecutiveSuccesses: int(data.ConsecutiveSuccesses.ValueInt64()),
}

result := window.Do(func(success int) bool {
result := window.Do(func(attempt int, success int) bool {
var stdout bytes.Buffer
var stderr bytes.Buffer

Expand Down

0 comments on commit a909dca

Please sign in to comment.