api: Add Notes field to service checks (#22397)

Co-authored-by: Piotr Kazmierczak <470696+pkazmierczak@users.noreply.github.com>
This commit is contained in:
nicoche
2024-06-10 16:59:49 +02:00
committed by GitHub
parent 1c976d126e
commit ffcb72bfe3
10 changed files with 60 additions and 0 deletions

3
.changelog/22397.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:improvement
api: Add support for setting Notes field for Consul health checks
```

View File

@@ -212,6 +212,7 @@ type ServiceCheck struct {
Interval time.Duration `hcl:"interval,optional"`
Timeout time.Duration `hcl:"timeout,optional"`
InitialStatus string `mapstructure:"initial_status" hcl:"initial_status,optional"`
Notes string `hcl:"notes,optional"`
TLSServerName string `mapstructure:"tls_server_name" hcl:"tls_server_name,optional"`
TLSSkipVerify bool `mapstructure:"tls_skip_verify" hcl:"tls_skip_verify,optional"`
Header map[string][]string `hcl:"header,block"`

View File

@@ -45,6 +45,7 @@ func InterpolateService(taskEnv *TaskEnv, origService *structs.Service) *structs
check.Protocol = taskEnv.ReplaceEnv(check.Protocol)
check.PortLabel = taskEnv.ReplaceEnv(check.PortLabel)
check.InitialStatus = taskEnv.ReplaceEnv(check.InitialStatus)
check.Notes = taskEnv.ReplaceEnv(check.Notes)
check.Method = taskEnv.ReplaceEnv(check.Method)
check.GRPCService = taskEnv.ReplaceEnv(check.GRPCService)
check.Header = interpolateMapStringSliceString(taskEnv, check.Header)

View File

@@ -43,6 +43,7 @@ func TestInterpolateServices(t *testing.T) {
Protocol: "${checkproto}",
PortLabel: "${checklabel}",
InitialStatus: "${checkstatus}",
Notes: "${checknotes}",
Method: "${checkmethod}",
Header: map[string][]string{
"${checkheaderk}": {"${checkheaderv}"},
@@ -71,6 +72,7 @@ func TestInterpolateServices(t *testing.T) {
"checkproto": "checkproto",
"checklabel": "checklabel",
"checkstatus": "checkstatus",
"checknotes": "checknotes",
"checkmethod": "checkmethod",
"checkheaderk": "checkheaderk",
"checkheaderv": "checkheaderv",
@@ -104,6 +106,7 @@ func TestInterpolateServices(t *testing.T) {
Protocol: "checkproto",
PortLabel: "checklabel",
InitialStatus: "checkstatus",
Notes: "checknotes",
Method: "checkmethod",
Header: map[string][]string{
"checkheaderk": {"checkheaderv"},

View File

@@ -1624,6 +1624,7 @@ func ApiServicesToStructs(in []*api.Service, group bool) []*structs.Service {
Interval: check.Interval,
Timeout: check.Timeout,
InitialStatus: check.InitialStatus,
Notes: check.Notes,
TLSServerName: check.TLSServerName,
TLSSkipVerify: check.TLSSkipVerify,
Header: check.Header,

View File

@@ -2729,6 +2729,7 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
Interval: 4 * time.Second,
Timeout: 2 * time.Second,
InitialStatus: "ok",
Notes: "this is a check",
CheckRestart: &api.CheckRestart{
Limit: 3,
IgnoreWarnings: true,
@@ -2838,6 +2839,7 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
Interval: 4 * time.Second,
Timeout: 2 * time.Second,
InitialStatus: "ok",
Notes: "this is a check",
SuccessBeforePassing: 3,
FailuresBeforeCritical: 4,
FailuresBeforeWarning: 2,
@@ -3163,6 +3165,7 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
Interval: 4 * time.Second,
Timeout: 2 * time.Second,
InitialStatus: "ok",
Notes: "this is a check",
CheckRestart: &structs.CheckRestart{
Grace: 11 * time.Second,
Limit: 3,
@@ -3274,6 +3277,7 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
Interval: 4 * time.Second,
Timeout: 2 * time.Second,
InitialStatus: "ok",
Notes: "this is a check",
GRPCService: "foo.Bar",
GRPCUseTLS: true,
SuccessBeforePassing: 3,

View File

@@ -1224,6 +1224,7 @@ func parseChecks(service *api.Service, checkObjs *ast.ObjectList) error {
"command",
"args",
"initial_status",
"notes",
"tls_skip_verify",
"header",
"method",

View File

@@ -3838,6 +3838,12 @@ func TestTaskGroupDiff(t *testing.T) {
Old: "foo",
New: "foo",
},
{
Type: DiffTypeNone,
Name: "Notes",
Old: "",
New: "",
},
{
Type: DiffTypeNone,
Name: "OnUpdate",
@@ -7607,6 +7613,7 @@ func TestTaskDiff(t *testing.T) {
Interval: 1 * time.Second,
Timeout: 1 * time.Second,
InitialStatus: "critical",
Notes: "a note",
Header: map[string][]string{
"Foo": {"bar"},
},
@@ -7635,6 +7642,7 @@ func TestTaskDiff(t *testing.T) {
Interval: 1 * time.Second,
Timeout: 1 * time.Second,
InitialStatus: "passing",
Notes: "another note",
Method: "POST",
Header: map[string][]string{
"Foo": {"bar", "baz"},
@@ -7788,6 +7796,12 @@ func TestTaskDiff(t *testing.T) {
Old: "foo",
New: "foo",
},
{
Type: DiffTypeEdited,
Name: "Notes",
Old: "a note",
New: "another note",
},
{
Type: DiffTypeEdited,
Name: "OnUpdate",

View File

@@ -68,6 +68,7 @@ type ServiceCheck struct {
Interval time.Duration // Interval of the check
Timeout time.Duration // Timeout of the response from the check before consul fails the check
InitialStatus string // Initial status of the check
Notes string // Specifies arbitrary information for humans. This is not used by Consul internally
TLSServerName string // ServerName to use for SNI and TLS verification when (Type=https and Protocol=https) or (Type=grpc and GRPCUseTLS=true)
TLSSkipVerify bool // Skip TLS verification when (type=https and Protocol=https) or (type=grpc and grpc_use_tls=true)
Method string // HTTP Method to use (GET by default)
@@ -161,6 +162,10 @@ func (sc *ServiceCheck) Equal(o *ServiceCheck) bool {
return false
}
if sc.Notes != o.Notes {
return false
}
if sc.Interval != o.Interval {
return false
}
@@ -454,6 +459,11 @@ func (sc *ServiceCheck) validateConsul() error {
return fmt.Errorf("failures_before_warning not supported for check of type %q", sc.Type)
}
// Arbitrary value, we could bump it if needed
if len(sc.Notes) > 255 {
return fmt.Errorf("notes must not be longer than 255 characters")
}
return nil
}
@@ -492,6 +502,7 @@ func (sc *ServiceCheck) Hash(serviceID string) string {
hashString(h, sc.Method)
hashString(h, sc.Body)
hashString(h, sc.OnUpdate)
hashString(h, sc.Notes)
// use name "true" to maintain ID stability
hashBool(h, sc.TLSSkipVerify, "true")

View File

@@ -5,6 +5,7 @@ package structs
import (
"errors"
"strings"
"testing"
"time"
@@ -1992,6 +1993,26 @@ func TestService_Validate(t *testing.T) {
},
expErr: false,
},
{
name: "provider consul with notes too long",
input: &Service{
Name: "testservice",
Provider: "consul",
PortLabel: "port",
Checks: []*ServiceCheck{
{
Name: "servicecheck",
Type: "http",
Path: "/",
Interval: 1 * time.Second,
Timeout: 3 * time.Second,
Notes: strings.Repeat("A", 256),
},
},
},
expErr: true,
expErrStr: "notes must not be longer than 255 characters",
},
}
for _, tc := range testCases {