Skip to content

Commit 773c935

Browse files
authored
Merge pull request #23 from tetratelabs/allow-tcp-failure
Allow TCP echo to fail
2 parents 486f26e + 5c8b83d commit 773c935

File tree

4 files changed

+88
-3
lines changed

4 files changed

+88
-3
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
.vscode/
12
dist/
23
terraform-provider-checkmate
34
terraform.tfstate*

docs/resources/tcp_echo.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,39 @@ resource "checkmate_tcp_echo" "example" {
3535
# Set a number of consecutive sucesses to make the check pass
3636
consecutive_successes = 5
3737
}
38+
39+
# In case you expect to be some kind of problem, and not getting
40+
# a response back, you can set `expect_failure` to true. In that case
41+
# you can skip `expected_message`.
42+
resource "checkmate_tcp_echo" "example" {
43+
# The hostname where the echo request will be sent
44+
host = "foo.bar"
45+
46+
# The TCP port at which the request will be sent
47+
port = 3002
48+
49+
# Message that will be sent to the TCP echo server
50+
message = "PROXY nonexistent.local:4242 foobartest"
51+
52+
# Expect this to fail
53+
expect_write_failure = true
54+
55+
# Set the connection timeout for the destination host, in milliseconds
56+
connection_timeout = 3000
57+
58+
# Set the per try timeout for the destination host, in milliseconds
59+
single_attempt_timeout = 2000
60+
61+
# Set a number of consecutive sucesses to make the check pass
62+
consecutive_successes = 5
63+
}
3864
```
3965

4066
<!-- schema generated by tfplugindocs -->
4167
## Schema
4268

4369
### Required
4470

45-
- `expected_message` (String) The message expected to be included in the echo response
4671
- `host` (String) The hostname where to send the TCP echo request to
4772
- `message` (String) The message to send in the echo request
4873
- `port` (Number) The port of the hostname where to send the TCP echo request
@@ -52,6 +77,8 @@ resource "checkmate_tcp_echo" "example" {
5277
- `connection_timeout` (Number) The timeout for stablishing a new TCP connection in milliseconds
5378
- `consecutive_successes` (Number) Number of consecutive successes required before the check is considered successful overall. Defaults to 1.
5479
- `create_anyway_on_check_failure` (Boolean) If false, the resource will fail to create if the check does not pass. If true, the resource will be created anyway. Defaults to false.
80+
- `expect_write_failure` (Boolean) Wether or not the check is expected to fail after successfully connecting to the target. If true, the check will be considered successful if it fails. Defaults to false.
81+
- `expected_message` (String) The message expected to be included in the echo response
5582
- `interval` (Number) Interval in milliseconds between attemps. Default 200
5683
- `keepers` (Map of String) Arbitrary map of string values that when changed will cause the check to run again.
5784
- `single_attempt_timeout` (Number) Timeout for an individual attempt. If exceeded, the attempt will be considered failure and potentially retried. Default 5000ms

examples/resources/checkmate_tcp_echo/resource.tf

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,29 @@ resource "checkmate_tcp_echo" "example" {
2020
# Set a number of consecutive sucesses to make the check pass
2121
consecutive_successes = 5
2222
}
23+
24+
# In case you expect to be some kind of problem, and not getting
25+
# a response back, you can set `expect_failure` to true. In that case
26+
# you can skip `expected_message`.
27+
resource "checkmate_tcp_echo" "example" {
28+
# The hostname where the echo request will be sent
29+
host = "foo.bar"
30+
31+
# The TCP port at which the request will be sent
32+
port = 3002
33+
34+
# Message that will be sent to the TCP echo server
35+
message = "PROXY nonexistent.local:4242 foobartest"
36+
37+
# Expect this to fail
38+
expect_write_failure = true
39+
40+
# Set the connection timeout for the destination host, in milliseconds
41+
connection_timeout = 3000
42+
43+
# Set the per try timeout for the destination host, in milliseconds
44+
single_attempt_timeout = 2000
45+
46+
# Set a number of consecutive sucesses to make the check pass
47+
consecutive_successes = 5
48+
}

pkg/provider/resource_tcp_echo.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ import (
2828
"github.com/hashicorp/terraform-plugin-framework/path"
2929
"github.com/hashicorp/terraform-plugin-framework/resource"
3030
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
31+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
3132
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
33+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
3234
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
3335
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
3436
"github.com/hashicorp/terraform-plugin-framework/types"
@@ -66,7 +68,17 @@ func (*TCPEchoResource) Schema(ctx context.Context, req resource.SchemaRequest,
6668
},
6769
"expected_message": schema.StringAttribute{
6870
MarkdownDescription: "The message expected to be included in the echo response",
69-
Required: true,
71+
Required: false,
72+
Optional: true,
73+
Computed: true,
74+
Default: stringdefault.StaticString(""),
75+
},
76+
"expect_write_failure": schema.BoolAttribute{
77+
MarkdownDescription: "Wether or not the check is expected to fail after successfully connecting to the target. If true, the check will be considered successful if it fails. Defaults to false.",
78+
Required: false,
79+
Optional: true,
80+
Computed: true,
81+
Default: booldefault.StaticBool(false),
7082
},
7183
"timeout": schema.Int64Attribute{
7284
MarkdownDescription: "Overall timeout in milliseconds for the check before giving up, default 10000",
@@ -125,6 +137,7 @@ type TCPEchoResourceModel struct {
125137
Port types.Int64 `tfsdk:"port"`
126138
Message types.String `tfsdk:"message"`
127139
ExpectedMessage types.String `tfsdk:"expected_message"`
140+
ExpectWriteFailure types.Bool `tfsdk:"expect_write_failure"`
128141
ConnectionTimeout types.Int64 `tfsdk:"connection_timeout"`
129142
SingleAttemptTimeout types.Int64 `tfsdk:"single_attempt_timeout"`
130143
Timeout types.Int64 `tfsdk:"timeout"`
@@ -160,6 +173,14 @@ func (r *TCPEchoResource) Create(ctx context.Context, req resource.CreateRequest
160173
}
161174

162175
func (r *TCPEchoResource) TCPEcho(ctx context.Context, data *TCPEchoResourceModel, diag *diag.Diagnostics) {
176+
if !data.ExpectWriteFailure.ValueBool() && data.ExpectedMessage.ValueString() == "" {
177+
tflog.Error(ctx, "expected_message is required when expect_failure is false")
178+
return
179+
}
180+
if data.ExpectedMessage.ValueString() != "" && data.ExpectWriteFailure.ValueBool() {
181+
tflog.Warn(ctx, "expected_message is ignored when expect_failure is true")
182+
}
183+
163184
data.Passed = types.BoolValue(false)
164185

165186
window := helpers.RetryWindow{
@@ -169,6 +190,7 @@ func (r *TCPEchoResource) TCPEcho(ctx context.Context, data *TCPEchoResourceMode
169190
}
170191

171192
result := window.Do(func(attempt int, success int) bool {
193+
exepctFailure := data.ExpectWriteFailure.ValueBool()
172194
destStr := data.Host.ValueString() + ":" + strconv.Itoa(int(data.Port.ValueInt64()))
173195

174196
d := net.Dialer{Timeout: time.Duration(data.ConnectionTimeout.ValueInt64()) * time.Millisecond}
@@ -187,17 +209,26 @@ func (r *TCPEchoResource) TCPEcho(ctx context.Context, data *TCPEchoResourceMode
187209

188210
deadlineDuration := time.Millisecond * time.Duration(data.SingleAttemptTimeout.ValueInt64())
189211
err = conn.SetDeadline(time.Now().Add(deadlineDuration))
190-
if err != nil {
212+
if err != nil && !exepctFailure {
191213
tflog.Warn(ctx, fmt.Sprintf("could not set connection deadline: %v", err.Error()))
192214
return false
193215
}
194216

195217
reply := make([]byte, 1024)
196218
_, err = conn.Read(reply)
197219
if err != nil {
220+
if exepctFailure {
221+
// We expected this
222+
return true
223+
}
198224
tflog.Warn(ctx, fmt.Sprintf("read from server failed: %v", err.Error()))
199225
return false
200226
}
227+
// At this point, if we expect failure, we can just return the check failed,
228+
// as we were expecting it to fail
229+
if exepctFailure {
230+
return false
231+
}
201232

202233
if !strings.Contains(string(reply), data.ExpectedMessage.ValueString()) {
203234
tflog.Warn(ctx, fmt.Sprintf("Got response %q, which does not include expected message %q", string(reply), data.ExpectedMessage.ValueString()))

0 commit comments

Comments
 (0)