UDP check for service stanza #12221 (#12722)

* UDP check for service stanza #12221

* add pass status on timeout condition

* delete useless files

* Update check_test.go

improve comment in test

* fix test

* fix requested changes and update TestRuntimeConfig_Sanitize.golden

* add freeport to TestCheckUDPCritical

* improve comment for CheckUDP struct

* fix requested changes

* fix requested changes

* fix requested changes

* add UDP to proto

* add UDP to proto and add a changelog

* add requested test on agent_endpoint_test.go

* add test for given endpoints

* fix failing tests

* add documentation for udp healthcheck

* regenerate proto using buf

* Update website/content/api-docs/agent/check.mdx

Co-authored-by: trujillo-adam <47586768+trujillo-adam@users.noreply.github.com>

* Update website/content/api-docs/agent/check.mdx

Co-authored-by: trujillo-adam <47586768+trujillo-adam@users.noreply.github.com>

* Update website/content/docs/discovery/checks.mdx

Co-authored-by: trujillo-adam <47586768+trujillo-adam@users.noreply.github.com>

* Update website/content/docs/ecs/configuration-reference.mdx

Co-authored-by: trujillo-adam <47586768+trujillo-adam@users.noreply.github.com>

* Update website/content/docs/ecs/configuration-reference.mdx

Co-authored-by: trujillo-adam <47586768+trujillo-adam@users.noreply.github.com>

* add debug echo

* add debug circle-ci

* add debug circle-ci bash

* use echo instead of status_stage

* remove debug and status from devtools script and use echo instead

* Update website/content/api-docs/agent/check.mdx

Co-authored-by: Jared Kirschner <85913323+jkirschner-hashicorp@users.noreply.github.com>

* fix test

* replace status_stage with status

* replace functions with echo

Co-authored-by: Dhia Ayachi <dhia@hashicorp.com>
Co-authored-by: trujillo-adam <47586768+trujillo-adam@users.noreply.github.com>
Co-authored-by: Jared Kirschner <85913323+jkirschner-hashicorp@users.noreply.github.com>
This commit is contained in:
Fulvio 2022-06-06 21:13:19 +02:00 committed by GitHub
parent 977b39cde1
commit f155ff347c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1458 additions and 145 deletions

3
.changelog/12722.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
checks: add UDP health checks..
```

View File

@ -248,6 +248,9 @@ type Agent struct {
// checkTCPs maps the check ID to an associated TCP check // checkTCPs maps the check ID to an associated TCP check
checkTCPs map[structs.CheckID]*checks.CheckTCP checkTCPs map[structs.CheckID]*checks.CheckTCP
// checkUDPs maps the check ID to an associated UDP check
checkUDPs map[structs.CheckID]*checks.CheckUDP
// checkGRPCs maps the check ID to an associated GRPC check // checkGRPCs maps the check ID to an associated GRPC check
checkGRPCs map[structs.CheckID]*checks.CheckGRPC checkGRPCs map[structs.CheckID]*checks.CheckGRPC
@ -401,6 +404,7 @@ func New(bd BaseDeps) (*Agent, error) {
checkHTTPs: make(map[structs.CheckID]*checks.CheckHTTP), checkHTTPs: make(map[structs.CheckID]*checks.CheckHTTP),
checkH2PINGs: make(map[structs.CheckID]*checks.CheckH2PING), checkH2PINGs: make(map[structs.CheckID]*checks.CheckH2PING),
checkTCPs: make(map[structs.CheckID]*checks.CheckTCP), checkTCPs: make(map[structs.CheckID]*checks.CheckTCP),
checkUDPs: make(map[structs.CheckID]*checks.CheckUDP),
checkGRPCs: make(map[structs.CheckID]*checks.CheckGRPC), checkGRPCs: make(map[structs.CheckID]*checks.CheckGRPC),
checkDockers: make(map[structs.CheckID]*checks.CheckDocker), checkDockers: make(map[structs.CheckID]*checks.CheckDocker),
checkAliases: make(map[structs.CheckID]*checks.CheckAlias), checkAliases: make(map[structs.CheckID]*checks.CheckAlias),
@ -1497,6 +1501,9 @@ func (a *Agent) ShutdownAgent() error {
for _, chk := range a.checkTCPs { for _, chk := range a.checkTCPs {
chk.Stop() chk.Stop()
} }
for _, chk := range a.checkUDPs {
chk.Stop()
}
for _, chk := range a.checkGRPCs { for _, chk := range a.checkGRPCs {
chk.Stop() chk.Stop()
} }
@ -2796,6 +2803,31 @@ func (a *Agent) addCheck(check *structs.HealthCheck, chkType *structs.CheckType,
tcp.Start() tcp.Start()
a.checkTCPs[cid] = tcp a.checkTCPs[cid] = tcp
case chkType.IsUDP():
if existing, ok := a.checkUDPs[cid]; ok {
existing.Stop()
delete(a.checkUDPs, cid)
}
if chkType.Interval < checks.MinInterval {
a.logger.Warn("check has interval below minimum",
"check", cid.String(),
"minimum_interval", checks.MinInterval,
)
chkType.Interval = checks.MinInterval
}
udp := &checks.CheckUDP{
CheckID: cid,
ServiceID: sid,
UDP: chkType.UDP,
Interval: chkType.Interval,
Timeout: chkType.Timeout,
Logger: a.logger,
StatusHandler: statusHandler,
}
udp.Start()
a.checkUDPs[cid] = udp
case chkType.IsGRPC(): case chkType.IsGRPC():
if existing, ok := a.checkGRPCs[cid]; ok { if existing, ok := a.checkGRPCs[cid]; ok {
existing.Stop() existing.Stop()
@ -3095,6 +3127,10 @@ func (a *Agent) cancelCheckMonitors(checkID structs.CheckID) {
check.Stop() check.Stop()
delete(a.checkTCPs, checkID) delete(a.checkTCPs, checkID)
} }
if check, ok := a.checkUDPs[checkID]; ok {
check.Stop()
delete(a.checkUDPs, checkID)
}
if check, ok := a.checkGRPCs[checkID]; ok { if check, ok := a.checkGRPCs[checkID]; ok {
check.Stop() check.Stop()
delete(a.checkGRPCs, checkID) delete(a.checkGRPCs, checkID)

View File

@ -2514,6 +2514,48 @@ func TestAgent_RegisterCheck(t *testing.T) {
} }
} }
func TestAgent_RegisterCheck_UDP(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
a := NewTestAgent(t, "")
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
args := &structs.CheckDefinition{
UDP: "1.1.1.1",
Name: "test",
Interval: 10 * time.Second,
}
req, _ := http.NewRequest("PUT", "/v1/agent/check/register?token=abc123", jsonReader(args))
resp := httptest.NewRecorder()
a.srv.h.ServeHTTP(resp, req)
require.Equal(t, http.StatusOK, resp.Code)
// Ensure we have a check mapping
checkID := structs.NewCheckID("test", nil)
if existing := a.State.Check(checkID); existing == nil {
t.Fatalf("missing test check")
}
if _, ok := a.checkUDPs[checkID]; !ok {
t.Fatalf("missing test check udp")
}
// Ensure the token was configured
if token := a.State.CheckToken(checkID); token == "" {
t.Fatalf("missing token")
}
// By default, checks start in critical state.
state := a.State.Check(checkID)
if state.Status != api.HealthCritical {
t.Fatalf("bad: %v", state)
}
}
// This verifies all the forms of the new args-style check that we need to // This verifies all the forms of the new args-style check that we need to
// support as a result of https://github.com/hashicorp/consul/issues/3587. // support as a result of https://github.com/hashicorp/consul/issues/3587.
func TestAgent_RegisterCheck_Scripts(t *testing.T) { func TestAgent_RegisterCheck_Scripts(t *testing.T) {
@ -3276,6 +3318,10 @@ func testAgent_RegisterService(t *testing.T, extraHCL string) {
{ {
TTL: 30 * time.Second, TTL: 30 * time.Second,
}, },
{
UDP: "1.1.1.1",
Interval: 5 * time.Second,
},
}, },
Weights: &structs.Weights{ Weights: &structs.Weights{
Passing: 100, Passing: 100,
@ -3307,12 +3353,12 @@ func testAgent_RegisterService(t *testing.T, extraHCL string) {
// Ensure we have a check mapping // Ensure we have a check mapping
checks := a.State.Checks(structs.WildcardEnterpriseMetaInDefaultPartition()) checks := a.State.Checks(structs.WildcardEnterpriseMetaInDefaultPartition())
if len(checks) != 3 { if len(checks) != 4 {
t.Fatalf("bad: %v", checks) t.Fatalf("bad: %v", checks)
} }
for _, c := range checks { for _, c := range checks {
if c.Type != "ttl" { if c.Type != "ttl" && c.Type != "udp" {
t.Fatalf("expected ttl check type, got %s", c.Type) t.Fatalf("expected ttl or udp check type, got %s", c.Type)
} }
} }
@ -3362,6 +3408,11 @@ func testAgent_RegisterService_ReRegister(t *testing.T, extraHCL string) {
CheckID: types.CheckID("check_2"), CheckID: types.CheckID("check_2"),
TTL: 30 * time.Second, TTL: 30 * time.Second,
}, },
{
CheckID: types.CheckID("check_3"),
UDP: "1.1.1.1",
Interval: 5 * time.Second,
},
}, },
Weights: &structs.Weights{ Weights: &structs.Weights{
Passing: 100, Passing: 100,
@ -3387,6 +3438,11 @@ func testAgent_RegisterService_ReRegister(t *testing.T, extraHCL string) {
CheckID: types.CheckID("check_3"), CheckID: types.CheckID("check_3"),
TTL: 30 * time.Second, TTL: 30 * time.Second,
}, },
{
CheckID: types.CheckID("check_3"),
UDP: "1.1.1.1",
Interval: 5 * time.Second,
},
}, },
Weights: &structs.Weights{ Weights: &structs.Weights{
Passing: 100, Passing: 100,
@ -3714,6 +3770,231 @@ func testAgent_RegisterService_TranslateKeys(t *testing.T, extraHCL string) {
} }
} }
func TestAgent_RegisterService_TranslateKeys_UDP(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Run("normal", func(t *testing.T) {
t.Parallel()
testAgent_RegisterService_TranslateKeys(t, "enable_central_service_config = false")
})
t.Run("service manager", func(t *testing.T) {
t.Parallel()
testAgent_RegisterService_TranslateKeys(t, "enable_central_service_config = true")
})
}
func testAgent_RegisterService_TranslateKeys_UDP(t *testing.T, extraHCL string) {
t.Helper()
tests := []struct {
ip string
expectedUDPCheckStart string
}{
{"127.0.0.1", "127.0.0.1:"}, // private network address
{"::1", "[::1]:"}, // shared address space
}
for _, tt := range tests {
t.Run(tt.ip, func(t *testing.T) {
a := NewTestAgent(t, `
connect {}
`+extraHCL)
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
json := `
{
"name":"test",
"port":8000,
"enable_tag_override": true,
"tagged_addresses": {
"lan": {
"address": "1.2.3.4",
"port": 5353
},
"wan": {
"address": "2.3.4.5",
"port": 53
}
},
"meta": {
"some": "meta",
"enable_tag_override": "meta is 'opaque' so should not get translated"
},
"kind": "connect-proxy",` +
// Note the uppercase P is important here - it ensures translation works
// correctly in case-insensitive way. Without it this test can pass even
// when translation is broken for other valid inputs.
`"Proxy": {
"destination_service_name": "web",
"destination_service_id": "web",
"local_service_port": 1234,
"local_service_address": "` + tt.ip + `",
"config": {
"destination_type": "proxy.config is 'opaque' so should not get translated"
},
"upstreams": [
{
"destination_type": "service",
"destination_namespace": "default",
"destination_partition": "default",
"destination_name": "db",
"local_bind_address": "` + tt.ip + `",
"local_bind_port": 1234,
"config": {
"destination_type": "proxy.upstreams.config is 'opaque' so should not get translated"
}
}
]
},
"connect": {
"sidecar_service": {
"name":"test-proxy",
"port":8001,
"enable_tag_override": true,
"meta": {
"some": "meta",
"enable_tag_override": "sidecar_service.meta is 'opaque' so should not get translated"
},
"kind": "connect-proxy",
"proxy": {
"destination_service_name": "test",
"destination_service_id": "test",
"local_service_port": 4321,
"local_service_address": "` + tt.ip + `",
"upstreams": [
{
"destination_type": "service",
"destination_namespace": "default",
"destination_partition": "default",
"destination_name": "db",
"local_bind_address": "` + tt.ip + `",
"local_bind_port": 1234,
"config": {
"destination_type": "sidecar_service.proxy.upstreams.config is 'opaque' so should not get translated"
}
}
]
}
}
},
"weights":{
"passing": 16
}
}`
req, _ := http.NewRequest("PUT", "/v1/agent/service/register", strings.NewReader(json))
rr := httptest.NewRecorder()
a.srv.h.ServeHTTP(rr, req)
require.Equal(t, 200, rr.Code, "body: %s", rr.Body)
svc := &structs.NodeService{
ID: "test",
Service: "test",
TaggedAddresses: map[string]structs.ServiceAddress{
"lan": {
Address: "1.2.3.4",
Port: 5353,
},
"wan": {
Address: "2.3.4.5",
Port: 53,
},
},
Meta: map[string]string{
"some": "meta",
"enable_tag_override": "meta is 'opaque' so should not get translated",
},
Port: 8000,
EnableTagOverride: true,
Weights: &structs.Weights{Passing: 16, Warning: 0},
Kind: structs.ServiceKindConnectProxy,
Proxy: structs.ConnectProxyConfig{
DestinationServiceName: "web",
DestinationServiceID: "web",
LocalServiceAddress: tt.ip,
LocalServicePort: 1234,
Config: map[string]interface{}{
"destination_type": "proxy.config is 'opaque' so should not get translated",
},
Upstreams: structs.Upstreams{
{
DestinationType: structs.UpstreamDestTypeService,
DestinationName: "db",
DestinationNamespace: "default",
DestinationPartition: "default",
LocalBindAddress: tt.ip,
LocalBindPort: 1234,
Config: map[string]interface{}{
"destination_type": "proxy.upstreams.config is 'opaque' so should not get translated",
},
},
},
},
Connect: structs.ServiceConnect{
// The sidecar service is nilled since it is only config sugar and
// shouldn't be represented in state. We assert that the translations
// there worked by inspecting the registered sidecar below.
SidecarService: nil,
},
EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
}
got := a.State.Service(structs.NewServiceID("test", nil))
require.Equal(t, svc, got)
sidecarSvc := &structs.NodeService{
Kind: structs.ServiceKindConnectProxy,
ID: "test-sidecar-proxy",
Service: "test-proxy",
Meta: map[string]string{
"some": "meta",
"enable_tag_override": "sidecar_service.meta is 'opaque' so should not get translated",
},
TaggedAddresses: map[string]structs.ServiceAddress{},
Port: 8001,
EnableTagOverride: true,
Weights: &structs.Weights{Passing: 1, Warning: 1},
LocallyRegisteredAsSidecar: true,
Proxy: structs.ConnectProxyConfig{
DestinationServiceName: "test",
DestinationServiceID: "test",
LocalServiceAddress: tt.ip,
LocalServicePort: 4321,
Upstreams: structs.Upstreams{
{
DestinationType: structs.UpstreamDestTypeService,
DestinationName: "db",
DestinationNamespace: "default",
DestinationPartition: "default",
LocalBindAddress: tt.ip,
LocalBindPort: 1234,
Config: map[string]interface{}{
"destination_type": "sidecar_service.proxy.upstreams.config is 'opaque' so should not get translated",
},
},
},
},
EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
}
gotSidecar := a.State.Service(structs.NewServiceID("test-sidecar-proxy", nil))
hasNoCorrectUDPCheck := true
for _, v := range a.checkUDPs {
if strings.HasPrefix(v.UDP, tt.expectedUDPCheckStart) {
hasNoCorrectUDPCheck = false
break
}
fmt.Println("UDP Check:= ", v)
}
if hasNoCorrectUDPCheck {
t.Fatalf("Did not find the expected UDP Healtcheck '%s' in %#v ", tt.expectedUDPCheckStart, a.checkUDPs)
}
require.Equal(t, sidecarSvc, gotSidecar)
})
}
}
func TestAgent_RegisterService_ACLDeny(t *testing.T) { func TestAgent_RegisterService_ACLDeny(t *testing.T) {
if testing.Short() { if testing.Short() {
t.Skip("too slow for testing.Short") t.Skip("too slow for testing.Short")
@ -4463,6 +4744,503 @@ func testAgent_RegisterServiceDeregisterService_Sidecar(t *testing.T, extraHCL s
} }
} }
// This tests local agent service registration with a sidecar service. Note we
// only test simple defaults for the sidecar here since the actual logic for
// handling sidecar defaults and port assignment is tested thoroughly in
// TestAgent_sidecarServiceFromNodeService. Note it also tests Deregister
// explicitly too since setup is identical.
func TestAgent_RegisterServiceDeregisterService_Sidecar_UDP(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Run("normal", func(t *testing.T) {
t.Parallel()
testAgent_RegisterServiceDeregisterService_Sidecar_UDP(t, "enable_central_service_config = false")
})
t.Run("service manager", func(t *testing.T) {
t.Parallel()
testAgent_RegisterServiceDeregisterService_Sidecar_UDP(t, "enable_central_service_config = true")
})
}
func testAgent_RegisterServiceDeregisterService_Sidecar_UDP(t *testing.T, extraHCL string) {
t.Helper()
tests := []struct {
name string
preRegister, preRegister2 *structs.NodeService
// Use raw JSON payloads rather than encoding to avoid subtleties with some
// internal representations and different ways they encode and decode. We
// rely on the payload being Unmarshalable to structs.ServiceDefinition
// directly.
json string
enableACL bool
tokenRules string
wantNS *structs.NodeService
wantErr string
wantSidecarIDLeftAfterDereg bool
assertStateFn func(t *testing.T, state *local.State)
}{
{
name: "sanity check no sidecar case",
json: `
{
"name": "web",
"port": 1111
}
`,
wantNS: nil,
wantErr: "",
},
{
name: "default sidecar",
json: `
{
"name": "web",
"port": 1111,
"connect": {
"SidecarService": {}
}
}
`,
wantNS: testDefaultSidecar("web", 1111),
wantErr: "",
},
{
name: "ACL OK defaults",
json: `
{
"name": "web",
"port": 1111,
"connect": {
"SidecarService": {}
}
}
`,
enableACL: true,
tokenRules: `
service "web-sidecar-proxy" {
policy = "write"
}
service "web" {
policy = "write"
}`,
wantNS: testDefaultSidecar("web", 1111),
wantErr: "",
},
{
name: "ACL denied",
json: `
{
"name": "web",
"port": 1111,
"connect": {
"SidecarService": {}
}
}
`,
enableACL: true,
tokenRules: ``, // No token rules means no valid token
wantNS: nil,
wantErr: "Permission denied",
},
{
name: "ACL OK for service but not for sidecar",
json: `
{
"name": "web",
"port": 1111,
"connect": {
"SidecarService": {}
}
}
`,
enableACL: true,
// This will become more common/reasonable when ACLs support exact match.
tokenRules: `
service "web-sidecar-proxy" {
policy = "deny"
}
service "web" {
policy = "write"
}`,
wantNS: nil,
wantErr: "Permission denied",
},
{
name: "ACL OK for service and sidecar but not sidecar's overridden destination",
json: `
{
"name": "web",
"port": 1111,
"connect": {
"SidecarService": {
"proxy": {
"DestinationServiceName": "foo"
}
}
}
}
`,
enableACL: true,
tokenRules: `
service "web-sidecar-proxy" {
policy = "write"
}
service "web" {
policy = "write"
}`,
wantNS: nil,
wantErr: "Permission denied",
},
{
name: "ACL OK for service but not for overridden sidecar",
json: `
{
"name": "web",
"port": 1111,
"connect": {
"SidecarService": {
"name": "foo-sidecar-proxy"
}
}
}
`,
enableACL: true,
tokenRules: `
service "web-sidecar-proxy" {
policy = "write"
}
service "web" {
policy = "write"
}`,
wantNS: nil,
wantErr: "Permission denied",
},
{
name: "ACL OK for service but and overridden for sidecar",
// This test ensures that if the sidecar embeds it's own token with
// different privs from the main request token it will be honored for the
// sidecar registration. We use the test root token since that should have
// permission.
json: `
{
"name": "web",
"port": 1111,
"connect": {
"SidecarService": {
"name": "foo",
"token": "root"
}
}
}
`,
enableACL: true,
tokenRules: `
service "web-sidecar-proxy" {
policy = "write"
}
service "web" {
policy = "write"
}`,
wantNS: testDefaultSidecar("web", 1111, func(ns *structs.NodeService) {
ns.Service = "foo"
}),
wantErr: "",
},
{
name: "invalid check definition in sidecar",
// Note no interval in the UDP check should fail validation
json: `
{
"name": "web",
"port": 1111,
"connect": {
"SidecarService": {
"check": {
"UDP": "foo"
}
}
}
}
`,
wantNS: nil,
wantErr: "invalid check in sidecar_service",
},
{
name: "invalid checks definitions in sidecar",
// Note no interval in the UDP check should fail validation
json: `
{
"name": "web",
"port": 1111,
"connect": {
"SidecarService": {
"checks": [{
"UDP": "foo"
}]
}
}
}
`,
wantNS: nil,
wantErr: "invalid check in sidecar_service",
},
{
name: "invalid check status in sidecar",
// Note no interval in the UDP check should fail validation
json: `
{
"name": "web",
"port": 1111,
"connect": {
"SidecarService": {
"check": {
"UDP": "foo",
"Interval": 10,
"Status": "unsupported-status"
}
}
}
}
`,
wantNS: nil,
wantErr: "Status for checks must 'passing', 'warning', 'critical'",
},
{
name: "invalid checks status in sidecar",
// Note no interval in the UDP check should fail validation
json: `
{
"name": "web",
"port": 1111,
"connect": {
"SidecarService": {
"checks": [{
"UDP": "foo",
"Interval": 10,
"Status": "unsupported-status"
}]
}
}
}
`,
wantNS: nil,
wantErr: "Status for checks must 'passing', 'warning', 'critical'",
},
{
name: "another service registered with same ID as a sidecar should not be deregistered",
// Add another service with the same ID that a sidecar for web would have
preRegister: &structs.NodeService{
ID: "web-sidecar-proxy",
Service: "fake-sidecar",
Port: 9999,
},
// Register web with NO SIDECAR
json: `
{
"name": "web",
"port": 1111
}
`,
// Note here that although the registration here didn't register it, we
// should still see the NodeService we pre-registered here.
wantNS: &structs.NodeService{
ID: "web-sidecar-proxy",
Service: "fake-sidecar",
Port: 9999,
TaggedAddresses: map[string]structs.ServiceAddress{},
Weights: &structs.Weights{
Passing: 1,
Warning: 1,
},
EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
},
// After we deregister the web service above, the fake sidecar with
// clashing ID SHOULD NOT have been removed since it wasn't part of the
// original registration.
wantSidecarIDLeftAfterDereg: true,
},
{
name: "updates to sidecar should work",
// Add a valid sidecar already registered
preRegister: &structs.NodeService{
ID: "web-sidecar-proxy",
Service: "web-sidecar-proxy",
LocallyRegisteredAsSidecar: true,
Port: 9999,
},
// Register web with Sidecar on different port
json: `
{
"name": "web",
"port": 1111,
"connect": {
"SidecarService": {
"Port": 6666
}
}
}
`,
// Note here that although the registration here didn't register it, we
// should still see the NodeService we pre-registered here.
wantNS: &structs.NodeService{
Kind: "connect-proxy",
ID: "web-sidecar-proxy",
Service: "web-sidecar-proxy",
LocallyRegisteredAsSidecar: true,
Port: 6666,
TaggedAddresses: map[string]structs.ServiceAddress{},
Weights: &structs.Weights{
Passing: 1,
Warning: 1,
},
Proxy: structs.ConnectProxyConfig{
DestinationServiceName: "web",
DestinationServiceID: "web",
LocalServiceAddress: "127.0.0.1",
LocalServicePort: 1111,
},
EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
},
},
{
name: "update that removes sidecar should NOT deregister it",
// Add web with a valid sidecar already registered
preRegister: &structs.NodeService{
ID: "web",
Service: "web",
Port: 1111,
},
preRegister2: testDefaultSidecar("web", 1111),
// Register (update) web and remove sidecar (and port for sanity check)
json: `
{
"name": "web",
"port": 2222
}
`,
// Sidecar should still be there such that API can update registration
// without accidentally removing a sidecar. This is equivalent to embedded
// checks which are not removed by just not being included in an update.
// We will document that sidecar registrations via API must be explicitiy
// deregistered.
wantNS: testDefaultSidecar("web", 1111),
// Sanity check the rest of the update happened though.
assertStateFn: func(t *testing.T, state *local.State) {
svc := state.Service(structs.NewServiceID("web", nil))
require.NotNil(t, svc)
require.Equal(t, 2222, svc.Port)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Constrain auto ports to 1 available to make it deterministic
hcl := `ports {
sidecar_min_port = 2222
sidecar_max_port = 2222
}
`
if tt.enableACL {
hcl = hcl + TestACLConfig()
}
a := NewTestAgent(t, hcl+" "+extraHCL)
defer a.Shutdown()
testrpc.WaitForLeader(t, a.RPC, "dc1")
if tt.preRegister != nil {
require.NoError(t, a.addServiceFromSource(tt.preRegister, nil, false, "", ConfigSourceLocal))
}
if tt.preRegister2 != nil {
require.NoError(t, a.addServiceFromSource(tt.preRegister2, nil, false, "", ConfigSourceLocal))
}
// Create an ACL token with require policy
var token string
if tt.enableACL && tt.tokenRules != "" {
token = testCreateToken(t, a, tt.tokenRules)
}
br := bytes.NewBufferString(tt.json)
req, _ := http.NewRequest("PUT", "/v1/agent/service/register?token="+token, br)
resp := httptest.NewRecorder()
a.srv.h.ServeHTTP(resp, req)
if tt.wantErr != "" {
require.Contains(t, strings.ToLower(resp.Body.String()), strings.ToLower(tt.wantErr))
return
}
require.Equal(t, 200, resp.Code, "request failed with body: %s",
resp.Body.String())
// Sanity the target service registration
svcs := a.State.AllServices()
// Parse the expected definition into a ServiceDefinition
var sd structs.ServiceDefinition
err := json.Unmarshal([]byte(tt.json), &sd)
require.NoError(t, err)
require.NotEmpty(t, sd.Name)
svcID := sd.ID
if svcID == "" {
svcID = sd.Name
}
sid := structs.NewServiceID(svcID, nil)
svc, ok := svcs[sid]
require.True(t, ok, "has service "+sid.String())
assert.Equal(t, sd.Name, svc.Service)
assert.Equal(t, sd.Port, svc.Port)
// Ensure that the actual registered service _doesn't_ still have it's
// sidecar info since it's duplicate and we don't want that synced up to
// the catalog or included in responses particularly - it's just
// registration syntax sugar.
assert.Nil(t, svc.Connect.SidecarService)
if tt.wantNS == nil {
// Sanity check that there was no service registered, we rely on there
// being no services at start of test so we can just use the count.
assert.Len(t, svcs, 1, "should be no sidecar registered")
return
}
// Ensure sidecar
svc, ok = svcs[structs.NewServiceID(tt.wantNS.ID, nil)]
require.True(t, ok, "no sidecar registered at "+tt.wantNS.ID)
assert.Equal(t, tt.wantNS, svc)
if tt.assertStateFn != nil {
tt.assertStateFn(t, a.State)
}
// Now verify deregistration also removes sidecar (if there was one and it
// was added via sidecar not just coincidental ID clash)
{
req := httptest.NewRequest("PUT",
"/v1/agent/service/deregister/"+svcID+"?token="+token, nil)
resp := httptest.NewRecorder()
a.srv.h.ServeHTTP(resp, req)
require.Equal(t, http.StatusOK, resp.Code)
svcs := a.State.AllServices()
_, ok = svcs[structs.NewServiceID(tt.wantNS.ID, nil)]
if tt.wantSidecarIDLeftAfterDereg {
require.True(t, ok, "removed non-sidecar service at "+tt.wantNS.ID)
} else {
require.False(t, ok, "sidecar not deregistered with service "+svcID)
}
}
})
}
}
// END HERE
// This tests that connect proxy validation is done for local agent // This tests that connect proxy validation is done for local agent
// registration. This doesn't need to test validation exhaustively since // registration. This doesn't need to test validation exhaustively since
// that is done via a table test in the structs package. // that is done via a table test in the structs package.

View File

@ -603,6 +603,63 @@ func TestCatalogRegister_checkRegistration(t *testing.T) {
}) })
} }
func TestCatalogRegister_checkRegistration_UDP(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
a := NewTestAgent(t, "")
defer a.Shutdown()
// Register node with a service and check
check := structs.HealthCheck{
Node: "foo",
CheckID: "foo-check",
Name: "foo check",
ServiceID: "api",
Definition: structs.HealthCheckDefinition{
UDP: "localhost:8888",
Interval: 5 * time.Second,
},
}
args := &structs.RegisterRequest{
Datacenter: "dc1",
Node: "foo",
Address: "127.0.0.1",
Service: &structs.NodeService{
Service: "api",
},
Check: &check,
}
var out struct{}
if err := a.RPC("Catalog.Register", args, &out); err != nil {
t.Fatalf("err: %v", err)
}
retry.Run(t, func(r *retry.R) {
req, _ := http.NewRequest("GET", "/v1/health/checks/api", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.HealthServiceChecks(resp, req)
if err != nil {
r.Fatalf("err: %v", err)
}
checks := obj.(structs.HealthChecks)
if len(checks) != 1 {
r.Fatalf("expected 1 check, got: %d", len(checks))
}
if checks[0].CheckID != check.CheckID {
r.Fatalf("expected check id %s, got %s", check.Type, checks[0].Type)
}
if checks[0].Type != "udp" {
r.Fatalf("expected check type udp, got %s", checks[0].Type)
}
})
}
func TestCatalogServiceNodes(t *testing.T) { func TestCatalogServiceNodes(t *testing.T) {
if testing.Short() { if testing.Short() {
t.Skip("too slow for testing.Short") t.Skip("too slow for testing.Short")

View File

@ -1,6 +1,7 @@
package checks package checks
import ( import (
"bufio"
"context" "context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
@ -703,6 +704,135 @@ func (c *CheckTCP) check() {
c.StatusHandler.updateCheck(c.CheckID, api.HealthPassing, fmt.Sprintf("TCP connect %s: Success", c.TCP)) c.StatusHandler.updateCheck(c.CheckID, api.HealthPassing, fmt.Sprintf("TCP connect %s: Success", c.TCP))
} }
// CheckUDP is used to periodically send a UDP datagram to determine the health of a given check.
// The check is passing if the connection succeeds, the response is bytes.Equal to the bytes passed
// in or if the error returned is a timeout error
// The check is critical if: the connection succeeds but the response is not equal to the bytes passed in,
// the connection succeeds but the error returned is not a timeout error or the connection fails
type CheckUDP struct {
CheckID structs.CheckID
ServiceID structs.ServiceID
UDP string
Message string
Interval time.Duration
Timeout time.Duration
Logger hclog.Logger
StatusHandler *StatusHandler
dialer *net.Dialer
stop bool
stopCh chan struct{}
stopLock sync.Mutex
}
func (c *CheckUDP) Start() {
c.stopLock.Lock()
defer c.stopLock.Unlock()
if c.dialer == nil {
// Create the socket dialer
c.dialer = &net.Dialer{
Timeout: 10 * time.Second,
}
if c.Timeout > 0 {
c.dialer.Timeout = c.Timeout
}
}
c.stop = false
c.stopCh = make(chan struct{})
go c.run()
}
func (c *CheckUDP) Stop() {
c.stopLock.Lock()
defer c.stopLock.Unlock()
if !c.stop {
c.stop = true
close(c.stopCh)
}
}
func (c *CheckUDP) run() {
// Get the randomized initial pause time
initialPauseTime := lib.RandomStagger(c.Interval)
next := time.After(initialPauseTime)
for {
select {
case <-next:
c.check()
next = time.After(c.Interval)
case <-c.stopCh:
return
}
}
}
func (c *CheckUDP) check() {
conn, err := c.dialer.Dial(`udp`, c.UDP)
if err != nil {
if e, ok := err.(net.Error); ok && e.Timeout() {
c.StatusHandler.updateCheck(c.CheckID, api.HealthPassing, fmt.Sprintf("UDP connect %s: Success", c.UDP))
return
} else {
c.Logger.Warn("Check socket connection failed",
"check", c.CheckID.String(),
"error", err,
)
c.StatusHandler.updateCheck(c.CheckID, api.HealthCritical, err.Error())
return
}
}
defer conn.Close()
n, err := fmt.Fprintf(conn, c.Message)
if err != nil {
c.Logger.Warn("Check socket write failed",
"check", c.CheckID.String(),
"error", err,
)
c.StatusHandler.updateCheck(c.CheckID, api.HealthCritical, err.Error())
return
}
if n != len(c.Message) {
c.Logger.Warn("Check socket short write",
"check", c.CheckID.String(),
"error", err,
)
c.StatusHandler.updateCheck(c.CheckID, api.HealthCritical, err.Error())
return
}
if err != nil {
c.Logger.Warn("Check socket write failed",
"check", c.CheckID.String(),
"error", err,
)
c.StatusHandler.updateCheck(c.CheckID, api.HealthCritical, err.Error())
return
}
_, err = bufio.NewReader(conn).Read(make([]byte, 1))
if err != nil {
if strings.Contains(err.Error(), "i/o timeout") {
c.StatusHandler.updateCheck(c.CheckID, api.HealthPassing, fmt.Sprintf("UDP connect %s: Success", c.UDP))
return
} else {
c.Logger.Warn("Check socket read failed",
"check", c.CheckID.String(),
"error", err,
)
c.StatusHandler.updateCheck(c.CheckID, api.HealthCritical, err.Error())
return
}
} else if err == nil {
c.StatusHandler.updateCheck(c.CheckID, api.HealthPassing, fmt.Sprintf("UDP connect %s: Success", c.UDP))
}
}
// CheckDocker is used to periodically invoke a script to // CheckDocker is used to periodically invoke a script to
// determine the health of an application running inside a // determine the health of an application running inside a
// Docker Container. We assume that the script is compatible // Docker Container. We assume that the script is compatible

View File

@ -2,20 +2,25 @@ package checks
import ( import (
"bytes" "bytes"
"context"
"fmt" "fmt"
"log"
"net" "net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
"reflect" "reflect"
"regexp" "regexp"
"strconv"
"strings" "strings"
"sync"
"testing" "testing"
"time" "time"
"github.com/hashicorp/consul/agent/mock" "github.com/hashicorp/consul/agent/mock"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/api" "github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/sdk/freeport"
"github.com/hashicorp/consul/sdk/testutil" "github.com/hashicorp/consul/sdk/testutil"
"github.com/hashicorp/consul/sdk/testutil/retry" "github.com/hashicorp/consul/sdk/testutil/retry"
"github.com/hashicorp/go-uuid" "github.com/hashicorp/go-uuid"
@ -1141,6 +1146,152 @@ func TestCheckTCPPassing(t *testing.T) {
tcpServer.Close() tcpServer.Close()
} }
func sendResponse(conn *net.UDPConn, addr *net.UDPAddr) {
_, err := conn.WriteToUDP([]byte("healthy"), addr)
if err != nil {
fmt.Printf("Couldn't send response %v", err)
}
}
func mockUDPServer(ctx context.Context, network string, port int) {
b := make([]byte, 1024)
addr := fmt.Sprintf(`127.0.0.1:%d`, port)
udpAddr, err := net.ResolveUDPAddr(network, addr)
if err != nil {
log.Fatal("Error resolving UDP address: ", err)
}
ser, err := net.ListenUDP("udp", udpAddr)
if err != nil {
log.Fatal("Error listening UDP: ", err)
}
defer ser.Close()
chClose := make(chan interface{})
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
for {
log.Print("Waiting for UDP message")
_, remoteaddr, err := ser.ReadFromUDP(b)
log.Printf("Read a message from %v %s \n", remoteaddr, b)
if err != nil {
log.Fatalf("Error reading from UDP %s", err.Error())
}
sendResponse(ser, remoteaddr)
select {
case <-chClose:
fmt.Println("cancelled")
wg.Done()
return
default:
}
}
}()
select {
case <-ctx.Done():
fmt.Println("cancelled")
close(chClose)
}
wg.Wait()
return
}
func expectUDPStatus(t *testing.T, udp string, status string) {
notif := mock.NewNotify()
logger := testutil.Logger(t)
statusHandler := NewStatusHandler(notif, logger, 0, 0, 0)
cid := structs.NewCheckID("foo", nil)
check := &CheckUDP{
CheckID: cid,
UDP: udp,
Interval: 10 * time.Millisecond,
Logger: logger,
StatusHandler: statusHandler,
}
check.Start()
defer check.Stop()
retry.Run(t, func(r *retry.R) {
if got, want := notif.Updates(cid), 2; got < want {
r.Fatalf("got %d updates want at least %d", got, want)
}
if got, want := notif.State(cid), status; got != want {
r.Fatalf("got state %q want %q", got, want)
}
})
}
func expectUDPTimeout(t *testing.T, udp string, status string) {
notif := mock.NewNotify()
logger := testutil.Logger(t)
statusHandler := NewStatusHandler(notif, logger, 0, 0, 0)
cid := structs.NewCheckID("foo", nil)
check := &CheckUDP{
CheckID: cid,
UDP: udp,
Interval: 10 * time.Millisecond,
Timeout: 5 * time.Nanosecond,
Logger: logger,
StatusHandler: statusHandler,
}
check.Start()
defer check.Stop()
retry.Run(t, func(r *retry.R) {
if got, want := notif.Updates(cid), 2; got < want {
r.Fatalf("got %d updates want at least %d", got, want)
}
if got, want := notif.State(cid), status; got != want {
r.Fatalf("got state %q want %q", got, want)
}
})
}
func TestCheckUDPTimeoutPassing(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
port := freeport.GetOne(t)
serverUrl := "127.0.0.1:" + strconv.Itoa(port)
go mockUDPServer(ctx, `udp`, port)
expectUDPTimeout(t, serverUrl, api.HealthPassing) // Should pass since timeout is handled as success, from specification
}
func TestCheckUDPCritical(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
port := freeport.GetOne(t)
notExistentPort := freeport.GetOne(t)
serverUrl := "127.0.0.1:" + strconv.Itoa(notExistentPort)
go mockUDPServer(ctx, `udp`, port)
expectUDPStatus(t, serverUrl, api.HealthCritical) // Should be unhealthy since we never connect to mocked udp server.
}
func TestCheckUDPPassing(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
port := freeport.GetOne(t)
serverUrl := "127.0.0.1:" + strconv.Itoa(port)
go mockUDPServer(ctx, `udp`, port)
expectUDPStatus(t, serverUrl, api.HealthPassing)
}
func TestCheckH2PING(t *testing.T) { func TestCheckH2PING(t *testing.T) {
t.Parallel() t.Parallel()

View File

@ -403,6 +403,7 @@ type CheckDefinition struct {
DisableRedirects *bool `mapstructure:"disable_redirects"` DisableRedirects *bool `mapstructure:"disable_redirects"`
OutputMaxSize *int `mapstructure:"output_max_size"` OutputMaxSize *int `mapstructure:"output_max_size"`
TCP *string `mapstructure:"tcp"` TCP *string `mapstructure:"tcp"`
UDP *string `mapstructure:"udp"`
Interval *string `mapstructure:"interval"` Interval *string `mapstructure:"interval"`
DockerContainerID *string `mapstructure:"docker_container_id" alias:"dockercontainerid"` DockerContainerID *string `mapstructure:"docker_container_id" alias:"dockercontainerid"`
Shell *string `mapstructure:"shell"` Shell *string `mapstructure:"shell"`

View File

@ -118,7 +118,8 @@
"TLSSkipVerify": false, "TLSSkipVerify": false,
"TTL": "0s", "TTL": "0s",
"Timeout": "0s", "Timeout": "0s",
"Token": "hidden" "Token": "hidden",
"UDP": ""
} }
], ],
"ClientAddrs": [], "ClientAddrs": [],
@ -324,7 +325,8 @@
"TLSServerName": "", "TLSServerName": "",
"TLSSkipVerify": false, "TLSSkipVerify": false,
"TTL": "0s", "TTL": "0s",
"Timeout": "0s" "Timeout": "0s",
"UDP": ""
}, },
"Checks": [], "Checks": [],
"Connect": null, "Connect": null,

View File

@ -33,6 +33,7 @@ type CheckDefinition struct {
Body string Body string
DisableRedirects bool DisableRedirects bool
TCP string TCP string
UDP string
Interval time.Duration Interval time.Duration
DockerContainerID string DockerContainerID string
Shell string Shell string
@ -215,6 +216,7 @@ func (c *CheckDefinition) CheckType() *CheckType {
DisableRedirects: c.DisableRedirects, DisableRedirects: c.DisableRedirects,
OutputMaxSize: c.OutputMaxSize, OutputMaxSize: c.OutputMaxSize,
TCP: c.TCP, TCP: c.TCP,
UDP: c.UDP,
Interval: c.Interval, Interval: c.Interval,
DockerContainerID: c.DockerContainerID, DockerContainerID: c.DockerContainerID,
Shell: c.Shell, Shell: c.Shell,

View File

@ -39,6 +39,7 @@ type CheckType struct {
Body string Body string
DisableRedirects bool DisableRedirects bool
TCP string TCP string
UDP string
Interval time.Duration Interval time.Duration
AliasNode string AliasNode string
AliasService string AliasService string
@ -179,13 +180,13 @@ func (t *CheckType) UnmarshalJSON(data []byte) (err error) {
// Validate returns an error message if the check is invalid // Validate returns an error message if the check is invalid
func (c *CheckType) Validate() error { func (c *CheckType) Validate() error {
intervalCheck := c.IsScript() || c.HTTP != "" || c.TCP != "" || c.GRPC != "" || c.H2PING != "" intervalCheck := c.IsScript() || c.HTTP != "" || c.TCP != "" || c.UDP != "" || c.GRPC != "" || c.H2PING != ""
if c.Interval > 0 && c.TTL > 0 { if c.Interval > 0 && c.TTL > 0 {
return fmt.Errorf("Interval and TTL cannot both be specified") return fmt.Errorf("Interval and TTL cannot both be specified")
} }
if intervalCheck && c.Interval <= 0 { if intervalCheck && c.Interval <= 0 {
return fmt.Errorf("Interval must be > 0 for Script, HTTP, H2PING, or TCP checks") return fmt.Errorf("Interval must be > 0 for Script, HTTP, H2PING, TCP or UDP checks")
} }
if intervalCheck && c.IsAlias() { if intervalCheck && c.IsAlias() {
return fmt.Errorf("Interval cannot be set for Alias checks") return fmt.Errorf("Interval cannot be set for Alias checks")
@ -241,6 +242,10 @@ func (c *CheckType) IsTCP() bool {
return c.TCP != "" && c.Interval > 0 return c.TCP != "" && c.Interval > 0
} }
func (c *CheckType) IsUDP() bool {
return c.UDP != "" && c.Interval > 0
}
// IsDocker returns true when checking a docker container. // IsDocker returns true when checking a docker container.
func (c *CheckType) IsDocker() bool { func (c *CheckType) IsDocker() bool {
return c.IsScript() && c.DockerContainerID != "" && c.Interval > 0 return c.IsScript() && c.DockerContainerID != "" && c.Interval > 0
@ -266,6 +271,8 @@ func (c *CheckType) Type() string {
return "ttl" return "ttl"
case c.IsTCP(): case c.IsTCP():
return "tcp" return "tcp"
case c.IsUDP():
return "udp"
case c.IsAlias(): case c.IsAlias():
return "alias" return "alias"
case c.IsDocker(): case c.IsDocker():

View File

@ -1670,7 +1670,7 @@ type HealthCheck struct {
ServiceID string // optional associated service ServiceID string // optional associated service
ServiceName string // optional service name ServiceName string // optional service name
ServiceTags []string // optional service tags ServiceTags []string // optional service tags
Type string // Check type: http/ttl/tcp/etc Type string // Check type: http/ttl/tcp/udp/etc
Interval string // from definition Interval string // from definition
Timeout string // from definition Timeout string // from definition
@ -1735,6 +1735,7 @@ type HealthCheckDefinition struct {
Body string `json:",omitempty"` Body string `json:",omitempty"`
DisableRedirects bool `json:",omitempty"` DisableRedirects bool `json:",omitempty"`
TCP string `json:",omitempty"` TCP string `json:",omitempty"`
UDP string `json:",omitempty"`
H2PING string `json:",omitempty"` H2PING string `json:",omitempty"`
H2PingUseTLS bool `json:",omitempty"` H2PingUseTLS bool `json:",omitempty"`
Interval time.Duration `json:",omitempty"` Interval time.Duration `json:",omitempty"`
@ -1885,6 +1886,7 @@ func (c *HealthCheck) CheckType() *CheckType {
Body: c.Definition.Body, Body: c.Definition.Body,
DisableRedirects: c.Definition.DisableRedirects, DisableRedirects: c.Definition.DisableRedirects,
TCP: c.Definition.TCP, TCP: c.Definition.TCP,
UDP: c.Definition.UDP,
H2PING: c.Definition.H2PING, H2PING: c.Definition.H2PING,
H2PingUseTLS: c.Definition.H2PingUseTLS, H2PingUseTLS: c.Definition.H2PingUseTLS,
Interval: c.Definition.Interval, Interval: c.Definition.Interval,

View File

@ -333,6 +333,7 @@ type AgentServiceCheck struct {
Method string `json:",omitempty"` Method string `json:",omitempty"`
Body string `json:",omitempty"` Body string `json:",omitempty"`
TCP string `json:",omitempty"` TCP string `json:",omitempty"`
UDP string `json:",omitempty"`
Status string `json:",omitempty"` Status string `json:",omitempty"`
Notes string `json:",omitempty"` Notes string `json:",omitempty"`
TLSServerName string `json:",omitempty"` TLSServerName string `json:",omitempty"`

View File

@ -62,6 +62,7 @@ type HealthCheckDefinition struct {
TLSServerName string TLSServerName string
TLSSkipVerify bool TLSSkipVerify bool
TCP string TCP string
UDP string
GRPC string GRPC string
GRPCUseTLS bool GRPCUseTLS bool
IntervalDuration time.Duration `json:"-"` IntervalDuration time.Duration `json:"-"`

View File

@ -56,6 +56,28 @@ func TestAPI_ClientTxn(t *testing.T) {
DeregisterCriticalServiceAfter: ReadableDuration(160 * time.Second), DeregisterCriticalServiceAfter: ReadableDuration(160 * time.Second),
}, },
}, },
{
CheckID: "bor",
Status: "critical",
Definition: HealthCheckDefinition{
UDP: "1.1.1.1",
Interval: ReadableDuration(5 * time.Second),
Timeout: ReadableDuration(10 * time.Second),
DeregisterCriticalServiceAfter: ReadableDuration(20 * time.Second),
},
Type: "udp",
},
{
CheckID: "bur",
Status: "passing",
Definition: HealthCheckDefinition{
UDP: "2.2.2.2",
Interval: ReadableDuration(5 * time.Second),
Timeout: ReadableDuration(10 * time.Second),
DeregisterCriticalServiceAfter: ReadableDuration(20 * time.Second),
},
Type: "udp",
},
}, },
} }
_, err = catalog.Register(reg, nil) _, err = catalog.Register(reg, nil)
@ -115,6 +137,18 @@ func TestAPI_ClientTxn(t *testing.T) {
Check: HealthCheck{Node: "foo", CheckID: "baz"}, Check: HealthCheck{Node: "foo", CheckID: "baz"},
}, },
}, },
&TxnOp{
Check: &CheckTxnOp{
Verb: CheckGet,
Check: HealthCheck{Node: "foo", CheckID: "bor"},
},
},
&TxnOp{
Check: &CheckTxnOp{
Verb: CheckGet,
Check: HealthCheck{Node: "foo", CheckID: "bur"},
},
},
} }
ok, ret, _, err := txn.Txn(ops, nil) ok, ret, _, err := txn.Txn(ops, nil)
if err != nil { if err != nil {
@ -141,7 +175,7 @@ func TestAPI_ClientTxn(t *testing.T) {
t.Fatalf("transaction failure") t.Fatalf("transaction failure")
} }
if ret == nil || len(ret.Errors) != 0 || len(ret.Results) != 6 { if ret == nil || len(ret.Errors) != 0 || len(ret.Results) != 8 {
t.Fatalf("bad: %v", ret) t.Fatalf("bad: %v", ret)
} }
expected := TxnResults{ expected := TxnResults{
@ -230,6 +264,48 @@ func TestAPI_ClientTxn(t *testing.T) {
ModifyIndex: ret.Results[4].Check.CreateIndex, ModifyIndex: ret.Results[4].Check.CreateIndex,
}, },
}, },
&TxnResult{
Check: &HealthCheck{
Node: "foo",
CheckID: "bor",
Status: "critical",
Definition: HealthCheckDefinition{
UDP: "1.1.1.1",
Interval: ReadableDuration(5 * time.Second),
IntervalDuration: 5 * time.Second,
Timeout: ReadableDuration(10 * time.Second),
TimeoutDuration: 10 * time.Second,
DeregisterCriticalServiceAfter: ReadableDuration(20 * time.Second),
DeregisterCriticalServiceAfterDuration: 20 * time.Second,
},
Type: "udp",
Partition: splitDefaultPartition,
Namespace: splitDefaultNamespace,
CreateIndex: ret.Results[4].Check.CreateIndex,
ModifyIndex: ret.Results[4].Check.CreateIndex,
},
},
&TxnResult{
Check: &HealthCheck{
Node: "foo",
CheckID: "bur",
Status: "passing",
Definition: HealthCheckDefinition{
UDP: "2.2.2.2",
Interval: ReadableDuration(5 * time.Second),
IntervalDuration: 5 * time.Second,
Timeout: ReadableDuration(10 * time.Second),
TimeoutDuration: 10 * time.Second,
DeregisterCriticalServiceAfter: ReadableDuration(20 * time.Second),
DeregisterCriticalServiceAfterDuration: 20 * time.Second,
},
Type: "udp",
Partition: splitDefaultPartition,
Namespace: splitDefaultNamespace,
CreateIndex: ret.Results[4].Check.CreateIndex,
ModifyIndex: ret.Results[4].Check.CreateIndex,
},
},
} }
require.Equal(t, expected, ret.Results) require.Equal(t, expected, ret.Results)

View File

@ -149,10 +149,10 @@ function install_unversioned_tool {
local install="$2" local install="$2"
if ! command -v "${command}" &>/dev/null ; then if ! command -v "${command}" &>/dev/null ; then
status_stage "installing tool: ${install}" echo "installing tool: ${install}"
go install "${install}" go install "${install}"
else else
debug "skipping tool: ${install} (installed)" echo "skipping tool: ${install} (installed)"
fi fi
return 0 return 0
@ -183,7 +183,7 @@ function install_versioned_tool {
err "dev version of '${command}' requested but not installed" err "dev version of '${command}' requested but not installed"
return 1 return 1
fi fi
status "skipping tool: ${installbase} (using development version)" echo "skipping tool: ${installbase} (using development version)"
return 0 return 0
fi fi
@ -210,10 +210,10 @@ function install_versioned_tool {
fi fi
if [[ -n $should_install ]]; then if [[ -n $should_install ]]; then
status_stage "installing tool (${install_reason}): ${install}" echo "installing tool (${install_reason}): ${install}"
go install "${install}" go install "${install}"
else else
debug "skipping tool: ${install} (installed)" echo "skipping tool: ${install} (installed)"
fi fi
return 0 return 0
} }

View File

@ -21,6 +21,7 @@ func CheckTypeToStructs(s *CheckType, t *structs.CheckType) {
t.Body = s.Body t.Body = s.Body
t.DisableRedirects = s.DisableRedirects t.DisableRedirects = s.DisableRedirects
t.TCP = s.TCP t.TCP = s.TCP
t.UDP = s.UDP
t.Interval = structs.DurationFromProto(s.Interval) t.Interval = structs.DurationFromProto(s.Interval)
t.AliasNode = s.AliasNode t.AliasNode = s.AliasNode
t.AliasService = s.AliasService t.AliasService = s.AliasService
@ -57,6 +58,7 @@ func CheckTypeFromStructs(t *structs.CheckType, s *CheckType) {
s.Body = t.Body s.Body = t.Body
s.DisableRedirects = t.DisableRedirects s.DisableRedirects = t.DisableRedirects
s.TCP = t.TCP s.TCP = t.TCP
s.UDP = t.UDP
s.Interval = structs.DurationToProto(t.Interval) s.Interval = structs.DurationToProto(t.Interval)
s.AliasNode = t.AliasNode s.AliasNode = t.AliasNode
s.AliasService = t.AliasService s.AliasService = t.AliasService
@ -138,6 +140,7 @@ func HealthCheckDefinitionToStructs(s *HealthCheckDefinition, t *structs.HealthC
t.Body = s.Body t.Body = s.Body
t.DisableRedirects = s.DisableRedirects t.DisableRedirects = s.DisableRedirects
t.TCP = s.TCP t.TCP = s.TCP
t.UDP = s.UDP
t.H2PING = s.H2PING t.H2PING = s.H2PING
t.H2PingUseTLS = s.H2PingUseTLS t.H2PingUseTLS = s.H2PingUseTLS
t.Interval = structs.DurationFromProto(s.Interval) t.Interval = structs.DurationFromProto(s.Interval)
@ -165,6 +168,7 @@ func HealthCheckDefinitionFromStructs(t *structs.HealthCheckDefinition, s *Healt
s.Body = t.Body s.Body = t.Body
s.DisableRedirects = t.DisableRedirects s.DisableRedirects = t.DisableRedirects
s.TCP = t.TCP s.TCP = t.TCP
s.UDP = t.UDP
s.H2PING = t.H2PING s.H2PING = t.H2PING
s.H2PingUseTLS = t.H2PingUseTLS s.H2PingUseTLS = t.H2PingUseTLS
s.Interval = structs.DurationToProto(t.Interval) s.Interval = structs.DurationToProto(t.Interval)

View File

@ -276,6 +276,7 @@ type HealthCheckDefinition struct {
Body string `protobuf:"bytes,18,opt,name=Body,proto3" json:"Body,omitempty"` Body string `protobuf:"bytes,18,opt,name=Body,proto3" json:"Body,omitempty"`
DisableRedirects bool `protobuf:"varint,22,opt,name=DisableRedirects,proto3" json:"DisableRedirects,omitempty"` DisableRedirects bool `protobuf:"varint,22,opt,name=DisableRedirects,proto3" json:"DisableRedirects,omitempty"`
TCP string `protobuf:"bytes,5,opt,name=TCP,proto3" json:"TCP,omitempty"` TCP string `protobuf:"bytes,5,opt,name=TCP,proto3" json:"TCP,omitempty"`
UDP string `protobuf:"bytes,23,opt,name=UDP,proto3" json:"UDP,omitempty"`
// mog: func-to=structs.DurationFromProto func-from=structs.DurationToProto // mog: func-to=structs.DurationFromProto func-from=structs.DurationToProto
Interval *durationpb.Duration `protobuf:"bytes,6,opt,name=Interval,proto3" json:"Interval,omitempty"` Interval *durationpb.Duration `protobuf:"bytes,6,opt,name=Interval,proto3" json:"Interval,omitempty"`
// mog: func-to=uint func-from=uint32 // mog: func-to=uint func-from=uint32
@ -385,6 +386,13 @@ func (x *HealthCheckDefinition) GetTCP() string {
return "" return ""
} }
func (x *HealthCheckDefinition) GetUDP() string {
if x != nil {
return x.UDP
}
return ""
}
func (x *HealthCheckDefinition) GetInterval() *durationpb.Duration { func (x *HealthCheckDefinition) GetInterval() *durationpb.Duration {
if x != nil { if x != nil {
return x.Interval return x.Interval
@ -513,6 +521,7 @@ type CheckType struct {
Body string `protobuf:"bytes,26,opt,name=Body,proto3" json:"Body,omitempty"` Body string `protobuf:"bytes,26,opt,name=Body,proto3" json:"Body,omitempty"`
DisableRedirects bool `protobuf:"varint,31,opt,name=DisableRedirects,proto3" json:"DisableRedirects,omitempty"` DisableRedirects bool `protobuf:"varint,31,opt,name=DisableRedirects,proto3" json:"DisableRedirects,omitempty"`
TCP string `protobuf:"bytes,8,opt,name=TCP,proto3" json:"TCP,omitempty"` TCP string `protobuf:"bytes,8,opt,name=TCP,proto3" json:"TCP,omitempty"`
UDP string `protobuf:"bytes,32,opt,name=UDP,proto3" json:"UDP,omitempty"`
// mog: func-to=structs.DurationFromProto func-from=structs.DurationToProto // mog: func-to=structs.DurationFromProto func-from=structs.DurationToProto
Interval *durationpb.Duration `protobuf:"bytes,9,opt,name=Interval,proto3" json:"Interval,omitempty"` Interval *durationpb.Duration `protobuf:"bytes,9,opt,name=Interval,proto3" json:"Interval,omitempty"`
AliasNode string `protobuf:"bytes,10,opt,name=AliasNode,proto3" json:"AliasNode,omitempty"` AliasNode string `protobuf:"bytes,10,opt,name=AliasNode,proto3" json:"AliasNode,omitempty"`
@ -656,6 +665,13 @@ func (x *CheckType) GetTCP() string {
return "" return ""
} }
func (x *CheckType) GetUDP() string {
if x != nil {
return x.UDP
}
return ""
}
func (x *CheckType) GetInterval() *durationpb.Duration { func (x *CheckType) GetInterval() *durationpb.Duration {
if x != nil { if x != nil {
return x.Interval return x.Interval
@ -843,7 +859,7 @@ var file_proto_pbservice_healthcheck_proto_rawDesc = []byte{
0x28, 0x09, 0x52, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x23, 0x0a, 0x0b, 0x28, 0x09, 0x52, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x23, 0x0a, 0x0b,
0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x56,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75,
0x65, 0x22, 0xae, 0x07, 0x0a, 0x15, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x65, 0x22, 0xc0, 0x07, 0x0a, 0x15, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63,
0x6b, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x48, 0x6b, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x48,
0x54, 0x54, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x54, 0x54, 0x50, 0x12, 0x54, 0x54, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x54, 0x54, 0x50, 0x12,
0x24, 0x0a, 0x0d, 0x54, 0x4c, 0x53, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x24, 0x0a, 0x0d, 0x54, 0x4c, 0x53, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65,
@ -861,134 +877,136 @@ var file_proto_pbservice_healthcheck_proto_rawDesc = []byte{
0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x73, 0x18, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x73, 0x18,
0x16, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x16, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65,
0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x18, 0x05, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x18, 0x05,
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x54, 0x43, 0x50, 0x12, 0x35, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x54, 0x43, 0x50, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x44, 0x50,
0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x44, 0x50, 0x12, 0x35, 0x0a, 0x08, 0x49,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x12, 0x24, 0x0a, 0x0d, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4d, 0x61, 0x78, 0x53, 0x69, 0x7a, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76,
0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4d, 0x61, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4d, 0x61, 0x78, 0x53,
0x61, 0x78, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x33, 0x0a, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x69, 0x7a, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x4f, 0x75, 0x74, 0x70, 0x75,
0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x74, 0x4d, 0x61, 0x78, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x33, 0x0a, 0x07, 0x54, 0x69, 0x6d, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x75, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6f, 0x6e, 0x52, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x61, 0x0a, 0x1e, 0x44, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61,
0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x61, 0x0a,
0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x66, 0x74, 0x65, 0x72, 0x18, 0x08, 0x20, 0x1e, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x72, 0x69, 0x74, 0x69,
0x63, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x66, 0x74, 0x65, 0x72, 0x18,
0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x52, 0x1e, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x72, 0x69, 0x74,
0x69, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x66, 0x74, 0x65, 0x72,
0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x73, 0x18, 0x0a,
0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x73,
0x12, 0x2c, 0x0a, 0x11, 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69,
0x6e, 0x65, 0x72, 0x49, 0x44, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x44, 0x6f, 0x63,
0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x12, 0x14,
0x0a, 0x05, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x53,
0x68, 0x65, 0x6c, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x48, 0x32, 0x50, 0x49, 0x4e, 0x47, 0x18, 0x14,
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x48, 0x32, 0x50, 0x49, 0x4e, 0x47, 0x12, 0x22, 0x0a, 0x0c,
0x48, 0x32, 0x50, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53, 0x18, 0x15, 0x20, 0x01,
0x28, 0x08, 0x52, 0x0c, 0x48, 0x32, 0x50, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53,
0x12, 0x12, 0x0a, 0x04, 0x47, 0x52, 0x50, 0x43, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x47, 0x52, 0x50, 0x43, 0x12, 0x1e, 0x0a, 0x0a, 0x47, 0x52, 0x50, 0x43, 0x55, 0x73, 0x65, 0x54,
0x4c, 0x53, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x47, 0x52, 0x50, 0x43, 0x55, 0x73,
0x65, 0x54, 0x4c, 0x53, 0x12, 0x1c, 0x0a, 0x09, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4e, 0x6f, 0x64,
0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4e, 0x6f,
0x64, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x53,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x11, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x1e, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03,
0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x54, 0x54, 0x4c, 0x1a, 0x4f, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74,
0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x66, 0x74, 0x65, 0x72, 0x12, 0x1e, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0a, 0x0a, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
0x28, 0x09, 0x52, 0x0a, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x2c, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x48, 0x65,
0x0a, 0x11, 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x72, 0x49, 0x44, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe2, 0x09, 0x0a, 0x09, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x79,
0x72, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x44, 0x18, 0x01, 0x20,
0x53, 0x68, 0x65, 0x6c, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x53, 0x68, 0x65, 0x01, 0x28, 0x09, 0x52, 0x07, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04,
0x6c, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x48, 0x32, 0x50, 0x49, 0x4e, 0x47, 0x18, 0x14, 0x20, 0x01, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65,
0x28, 0x09, 0x52, 0x06, 0x48, 0x32, 0x50, 0x49, 0x4e, 0x47, 0x12, 0x22, 0x0a, 0x0c, 0x48, 0x32, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x50, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x6f, 0x74, 0x65,
0x52, 0x0c, 0x48, 0x32, 0x50, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53, 0x12, 0x12, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4e, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x1e,
0x0a, 0x04, 0x47, 0x52, 0x50, 0x43, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x47, 0x52, 0x0a, 0x0a, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03,
0x50, 0x43, 0x12, 0x1e, 0x0a, 0x0a, 0x47, 0x52, 0x50, 0x43, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53, 0x28, 0x09, 0x52, 0x0a, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x12,
0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x47, 0x52, 0x50, 0x43, 0x55, 0x73, 0x65, 0x54, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x54,
0x4c, 0x53, 0x12, 0x1c, 0x0a, 0x09, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x18, 0x54, 0x50, 0x12, 0x36, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x14, 0x20, 0x03,
0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x43, 0x68, 0x65,
0x12, 0x22, 0x0a, 0x0c, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x63, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74,
0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x53, 0x65, 0x72, 0x72, 0x79, 0x52, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x65,
0x76, 0x69, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x11, 0x20, 0x01, 0x28, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x4d, 0x65, 0x74, 0x68,
0x6f, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x42, 0x6f, 0x64, 0x79, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c,
0x65, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x73, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x08,
0x52, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63,
0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52,
0x03, 0x54, 0x43, 0x50, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x18, 0x20, 0x20, 0x01, 0x28,
0x09, 0x52, 0x03, 0x55, 0x44, 0x50, 0x12, 0x35, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76,
0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x52, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1c, 0x0a,
0x09, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09,
0x52, 0x09, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x41,
0x6c, 0x69, 0x61, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0c, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
0x2c, 0x0a, 0x11, 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
0x65, 0x72, 0x49, 0x44, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x44, 0x6f, 0x63, 0x6b,
0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x12, 0x14, 0x0a,
0x05, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x53, 0x68,
0x65, 0x6c, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x48, 0x32, 0x50, 0x49, 0x4e, 0x47, 0x18, 0x1c, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x48, 0x32, 0x50, 0x49, 0x4e, 0x47, 0x12, 0x22, 0x0a, 0x0c, 0x48,
0x32, 0x50, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53, 0x18, 0x1e, 0x20, 0x01, 0x28,
0x08, 0x52, 0x0c, 0x48, 0x32, 0x50, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53, 0x12,
0x12, 0x0a, 0x04, 0x47, 0x52, 0x50, 0x43, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x47,
0x52, 0x50, 0x43, 0x12, 0x1e, 0x0a, 0x0a, 0x47, 0x52, 0x50, 0x43, 0x55, 0x73, 0x65, 0x54, 0x4c,
0x53, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x47, 0x52, 0x50, 0x43, 0x55, 0x73, 0x65,
0x54, 0x4c, 0x53, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x4c, 0x53, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
0x4e, 0x61, 0x6d, 0x65, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x54, 0x4c, 0x53, 0x53,
0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x4c, 0x53,
0x53, 0x6b, 0x69, 0x70, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08,
0x52, 0x0d, 0x54, 0x4c, 0x53, 0x53, 0x6b, 0x69, 0x70, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12,
0x33, 0x0a, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x54, 0x69, 0x6d,
0x65, 0x6f, 0x75, 0x74, 0x12, 0x2b, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x12, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x54, 0x54, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x54, 0x54,
0x4c, 0x1a, 0x4f, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x4c, 0x12, 0x32, 0x0a, 0x14, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x65, 0x66, 0x6f,
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x72, 0x65, 0x50, 0x61, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x15, 0x20, 0x01, 0x28, 0x05, 0x52,
0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64,
0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
0x38, 0x01, 0x22, 0xd0, 0x09, 0x0a, 0x09, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x79, 0x70, 0x65,
0x12, 0x18, 0x0a, 0x07, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x07, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61,
0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16,
0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x6f, 0x74, 0x65, 0x73, 0x18,
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4e, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a,
0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09,
0x52, 0x0a, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x12, 0x0a, 0x04,
0x48, 0x54, 0x54, 0x50, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x54, 0x54, 0x50,
0x12, 0x36, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x1e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b,
0x54, 0x79, 0x70, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79,
0x52, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x68,
0x6f, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64,
0x12, 0x12, 0x0a, 0x04, 0x42, 0x6f, 0x64, 0x79, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x42, 0x6f, 0x64, 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52,
0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x73, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10,
0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x73,
0x12, 0x10, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x54,
0x43, 0x50, 0x12, 0x35, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x09,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x41, 0x6c, 0x69,
0x61, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x41, 0x6c,
0x69, 0x61, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x41, 0x6c, 0x69, 0x61, 0x73,
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x41,
0x6c, 0x69, 0x61, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x44,
0x6f, 0x63, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44,
0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x43, 0x6f,
0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x68, 0x65,
0x6c, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x12,
0x16, 0x0a, 0x06, 0x48, 0x32, 0x50, 0x49, 0x4e, 0x47, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x09, 0x52,
0x06, 0x48, 0x32, 0x50, 0x49, 0x4e, 0x47, 0x12, 0x22, 0x0a, 0x0c, 0x48, 0x32, 0x50, 0x69, 0x6e,
0x67, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x48,
0x32, 0x50, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53, 0x12, 0x12, 0x0a, 0x04, 0x47,
0x52, 0x50, 0x43, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x47, 0x52, 0x50, 0x43, 0x12,
0x1e, 0x0a, 0x0a, 0x47, 0x52, 0x50, 0x43, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53, 0x18, 0x0f, 0x20,
0x01, 0x28, 0x08, 0x52, 0x0a, 0x47, 0x52, 0x50, 0x43, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53, 0x12,
0x24, 0x0a, 0x0d, 0x54, 0x4c, 0x53, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65,
0x18, 0x1b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x54, 0x4c, 0x53, 0x53, 0x65, 0x72, 0x76, 0x65,
0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x4c, 0x53, 0x53, 0x6b, 0x69, 0x70,
0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x54, 0x4c,
0x53, 0x53, 0x6b, 0x69, 0x70, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x33, 0x0a, 0x07, 0x54,
0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44,
0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
0x12, 0x2b, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, 0x32, 0x0a,
0x14, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x50, 0x61, 0x14, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x50, 0x61,
0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x15, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x53, 0x75, 0x63, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x15, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65,
0x63, 0x65, 0x73, 0x73, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x50, 0x61, 0x73, 0x73, 0x69, 0x6e, 0x73, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x1d,
0x67, 0x12, 0x34, 0x0a, 0x15, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x73, 0x42, 0x65, 0x66, 0x20, 0x01, 0x28, 0x05, 0x52, 0x15, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x73, 0x42, 0x65,
0x6f, 0x72, 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x05, 0x66, 0x6f, 0x72, 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x36, 0x0a, 0x16, 0x46,
0x52, 0x15, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x73, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x73, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x43, 0x72, 0x69,
0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x36, 0x0a, 0x16, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x18, 0x16, 0x20, 0x01, 0x28, 0x05, 0x52, 0x16, 0x46, 0x61, 0x69,
0x72, 0x65, 0x73, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x75, 0x72, 0x65, 0x73, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x43, 0x72, 0x69, 0x74, 0x69,
0x6c, 0x18, 0x16, 0x20, 0x01, 0x28, 0x05, 0x52, 0x16, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x63, 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x48, 0x54, 0x54, 0x50,
0x73, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x12, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x48, 0x54, 0x54,
0x1c, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x48, 0x54, 0x54, 0x50, 0x18, 0x17, 0x20, 0x01, 0x50, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, 0x52, 0x50, 0x43, 0x18, 0x18,
0x28, 0x09, 0x52, 0x09, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x48, 0x54, 0x54, 0x50, 0x12, 0x1c, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, 0x52, 0x50, 0x43, 0x12,
0x09, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, 0x52, 0x50, 0x43, 0x18, 0x18, 0x20, 0x01, 0x28, 0x09, 0x61, 0x0a, 0x1e, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x72, 0x69,
0x52, 0x09, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, 0x52, 0x50, 0x43, 0x12, 0x61, 0x0a, 0x1e, 0x44, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x66, 0x74, 0x65,
0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x72, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x66, 0x74, 0x65, 0x72, 0x18, 0x13, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69,
0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x1e, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x72,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x1e, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x66, 0x74,
0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4d, 0x61, 0x78, 0x53,
0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x66, 0x74, 0x65, 0x72, 0x12, 0x24, 0x69, 0x7a, 0x65, 0x18, 0x19, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x4f, 0x75, 0x74, 0x70, 0x75,
0x0a, 0x0d, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4d, 0x61, 0x78, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x74, 0x4d, 0x61, 0x78, 0x53, 0x69, 0x7a, 0x65, 0x1a, 0x4f, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64,
0x19, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4d, 0x61, 0x78, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
0x53, 0x69, 0x7a, 0x65, 0x1a, 0x4f, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c,
0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69,
0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x48, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x88, 0x01, 0x0a, 0x0b, 0x63, 0x6f,
0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x6d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x10, 0x48, 0x65, 0x61, 0x6c, 0x74,
0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x88, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x65, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b, 0x67,
0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x10, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63,
0x63, 0x6b, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x2f, 0x70, 0x62, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0xa2, 0x02, 0x03, 0x53, 0x58, 0x58,
0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x73, 0xaa, 0x02, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0xca, 0x02, 0x07, 0x53, 0x65, 0x72,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0xa2, 0x02, 0x03, 0x53, 0x58, 0x58, 0xaa, 0x02, 0x07, 0x53, 0x76, 0x69, 0x63, 0x65, 0xe2, 0x02, 0x13, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5c, 0x47,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0xca, 0x02, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x53, 0x65, 0x72,
0xe2, 0x02, 0x13, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

View File

@ -63,6 +63,7 @@ message HealthCheckDefinition {
string Body = 18; string Body = 18;
bool DisableRedirects = 22; bool DisableRedirects = 22;
string TCP = 5; string TCP = 5;
string UDP = 23;
// mog: func-to=structs.DurationFromProto func-from=structs.DurationToProto // mog: func-to=structs.DurationFromProto func-from=structs.DurationToProto
google.protobuf.Duration Interval = 6; google.protobuf.Duration Interval = 6;
@ -112,6 +113,7 @@ message CheckType {
string Body = 26; string Body = 26;
bool DisableRedirects = 31; bool DisableRedirects = 31;
string TCP = 8; string TCP = 8;
string UDP = 32;
// mog: func-to=structs.DurationFromProto func-from=structs.DurationToProto // mog: func-to=structs.DurationFromProto func-from=structs.DurationToProto
google.protobuf.Duration Interval = 9; google.protobuf.Duration Interval = 9;
@ -125,6 +127,7 @@ message CheckType {
bool GRPCUseTLS = 15; bool GRPCUseTLS = 15;
string TLSServerName = 27; string TLSServerName = 27;
bool TLSSkipVerify = 16; bool TLSSkipVerify = 16;
// mog: func-to=structs.DurationFromProto func-from=structs.DurationToProto // mog: func-to=structs.DurationFromProto func-from=structs.DurationToProto
google.protobuf.Duration Timeout = 17; google.protobuf.Duration Timeout = 17;
// mog: func-to=structs.DurationFromProto func-from=structs.DurationToProto // mog: func-to=structs.DurationFromProto func-from=structs.DurationToProto

View File

@ -98,7 +98,7 @@ the following selectors and filter operations being supported:
## Register Check ## Register Check
This endpoint adds a new check to the local agent. Checks may be of script, This endpoint adds a new check to the local agent. Checks may be of script,
HTTP, TCP, or TTL type. The agent is responsible for managing the status of the HTTP, TCP, UDP, or TTL type. The agent is responsible for managing the status of the
check and keeping the Catalog in sync. check and keeping the Catalog in sync.
| Method | Path | Produces | | Method | Path | Produces |
@ -133,7 +133,7 @@ The table below shows this endpoint's support for
one of several [other methods to specify the namespace](#methods-to-specify-namespace). one of several [other methods to specify the namespace](#methods-to-specify-namespace).
- `Interval` `(string: "")` - Specifies the frequency at which to run this - `Interval` `(string: "")` - Specifies the frequency at which to run this
check. This is required for HTTP and TCP checks. check. This is required for HTTP, TCP, and UDP checks.
- `Notes` `(string: "")` - Specifies arbitrary information for humans. This is - `Notes` `(string: "")` - Specifies arbitrary information for humans. This is
not used by Consul internally. not used by Consul internally.
@ -212,7 +212,7 @@ The table below shows this endpoint's support for
be set for `HTTP` checks. Each header can have multiple values. be set for `HTTP` checks. Each header can have multiple values.
- `Timeout` `(duration: 10s)` - Specifies a timeout for outgoing connections in the - `Timeout` `(duration: 10s)` - Specifies a timeout for outgoing connections in the
case of a Script, HTTP, TCP, or gRPC check. Can be specified in the form of "10s" case of a Script, HTTP, TCP, UDP, or gRPC check. Can be specified in the form of "10s"
or "5m" (i.e., 10 seconds or 5 minutes, respectively). or "5m" (i.e., 10 seconds or 5 minutes, respectively).
- `OutputMaxSize` `(positive int: 4096)` - Allow to put a maximum size of text - `OutputMaxSize` `(positive int: 4096)` - Allow to put a maximum size of text
@ -237,6 +237,11 @@ The table below shows this endpoint's support for
made to both addresses, and the first successful connection attempt will made to both addresses, and the first successful connection attempt will
result in a successful check. result in a successful check.
- `UDP` `(string: "")` - Specifies a `UDP` IP address/hostname and port.
The check sends datagrams to the value specified at the interval specified in the `Interval` configuration.
If the datagram is sent successfully or a timeout is returned, the check is set to the `passing` state.
The check is logged as `critical` if the datagram is sent unsuccessfully.
- `TTL` `(duration: 10s)` - Specifies this is a TTL check, and the TTL endpoint - `TTL` `(duration: 10s)` - Specifies this is a TTL check, and the TTL endpoint
must be used periodically to update the state of the check. If the check is not must be used periodically to update the state of the check. If the check is not
set to passing within the specified duration, then the check will be set to the failed state. set to passing within the specified duration, then the check will be set to the failed state.

View File

@ -85,6 +85,12 @@ There are several different kinds of checks:
It is possible to configure a custom TCP check timeout value by specifying the It is possible to configure a custom TCP check timeout value by specifying the
`timeout` field in the check definition. `timeout` field in the check definition.
- `UDP + Interval` - These checks direct the client to periodically send UDP datagrams
to the specified IP/hostname and port. The duration specified in the `interval` field sets the amount of time
between attempts, such as `30s` to indicate 30 seconds. The check is logged as healthy if any response from the UDP server is received. Any other result sets the status to `critical`.
The default interval for, UDP checks is `10s`, but you can configure a custom UDP check timeout value by specifying the
`timeout` field in the check definition. If any timeout on read exists, the check is still considered healthy.
- `Time to Live (TTL)` ((#ttl)) - These checks retain their last known state - `Time to Live (TTL)` ((#ttl)) - These checks retain their last known state
for a given TTL. The state of the check must be updated periodically over the HTTP for a given TTL. The state of the check must be updated periodically over the HTTP
interface. If an external system fails to update the status within a given TTL, interface. If an external system fails to update the status within a given TTL,
@ -243,6 +249,35 @@ check = {
</CodeTabs> </CodeTabs>
A UDP check:
<CodeTabs heading="UDP Check">
```hcl
check = {
id = "dns"
name = "DNS UDP on port 53"
udp = "localhost:53"
interval = "10s"
timeout = "1s"
}
```
```json
{
"check": {
"id": "dns",
"name": "DNS UDP on port 53",
"udp": "localhost:53",
"interval": "10s",
"timeout": "1s"
}
}
```
</CodeTabs>
A TTL check: A TTL check:
<CodeTabs heading="TTL Check"> <CodeTabs heading="TTL Check">
@ -429,7 +464,7 @@ used for any interaction with the catalog for the check, including
For Alias checks, this token is used if a remote blocking query is necessary For Alias checks, this token is used if a remote blocking query is necessary
to watch the state of the aliased node or service. to watch the state of the aliased node or service.
Script, TCP, HTTP, Docker, and gRPC checks must include an `interval` field. This Script, TCP, UDP, HTTP, Docker, and gRPC checks must include an `interval` field. This
field is parsed by Go's `time` package, and has the following field is parsed by Go's `time` package, and has the following
[formatting specification](https://golang.org/pkg/time/#ParseDuration): [formatting specification](https://golang.org/pkg/time/#ParseDuration):

View File

@ -82,14 +82,15 @@ Defines the Consul checks for the service. Each check may contain these fields.
| `h2pingUseTls` | `boolean` | optional | Specifies whether TLS is used for an h2ping check. | | `h2pingUseTls` | `boolean` | optional | Specifies whether TLS is used for an h2ping check. |
| `header` | `object` | optional | Specifies a set of headers that should be set for HTTP checks. Each header can have multiple values. | | `header` | `object` | optional | Specifies a set of headers that should be set for HTTP checks. Each header can have multiple values. |
| `http` | `string` | optional | Specifies this is an HTTP check. Must be a URL against which request is performed every `interval`. | | `http` | `string` | optional | Specifies this is an HTTP check. Must be a URL against which request is performed every `interval`. |
| `interval` | `string` | optional | Specifies the frequency at which to run this check. Required for HTTP and TCP checks. | | `interval` | `string` | optional | Specifies the frequency at which to run this check. Required for HTTP, TCP, and UDP checks. |
| `method` | `string` | optional | Specifies the HTTP method to be used for an HTTP check. When no value is specified, `GET` is used. | | `method` | `string` | optional | Specifies the HTTP method to be used for an HTTP check. When no value is specified, `GET` is used. |
| `name` | `string` | optional | The name of the check. | | `name` | `string` | optional | The name of the check. |
| `notes` | `string` | optional | Specifies arbitrary information for humans. This is not used by Consul internally. | | `notes` | `string` | optional | Specifies arbitrary information for humans. This is not used by Consul internally. |
| `status` | `string` | optional | Specifies the initial status the health check. Must be one of `passing`, `warning`, `critical`, `maintenance`, or`null`. | | `status` | `string` | optional | Specifies the initial status the health check. Must be one of `passing`, `warning`, `critical`, `maintenance`, or`null`. |
| `successBeforePassing` | `integer` | optional | Specifies the number of consecutive successful results required before check status transitions to passing. | | `successBeforePassing` | `integer` | optional | Specifies the number of consecutive successful results required before check status transitions to passing. |
| `tcp` | `string` | optional | Specifies this is a TCP check. Must be an IP/hostname plus port to which a TCP connection is made every `interval`. | | `tcp` | `string` | optional | Specifies this is a TCP check. Must be an IP/hostname plus port to which a TCP connection is made every `interval`. |
| `timeout` | `string` | optional | Specifies a timeout for outgoing connections in the case of a Script, HTTP, TCP, or gRPC check. Must be a duration string, such as `10s` or `5m`. | | `udp` | `string` | optional | Specifies this is a UDP check. Must be an IP/hostname plus port to which UDP datagrams are sent every `interval`. |
| `timeout` | `string` | optional | Specifies a timeout for outgoing connections. Applies to script, HTTP, TCP, UDP, and gRPC checks. Must be a duration string, such as `10s` or `5m`. |
| `tlsServerName` | `string` | optional | Specifies an optional string used to set the SNI host when connecting via TLS. | | `tlsServerName` | `string` | optional | Specifies an optional string used to set the SNI host when connecting via TLS. |
| `tlsSkipVerify` | `boolean` | optional | Specifies if the certificate for an HTTPS check should not be verified. | | `tlsSkipVerify` | `boolean` | optional | Specifies if the certificate for an HTTPS check should not be verified. |
| `ttl` | `string` | optional | Specifies this is a TTL check. Must be a duration string, such as `10s` or `5m`. | | `ttl` | `string` | optional | Specifies this is a TTL check. Must be a duration string, such as `10s` or `5m`. |