Skip to content

Commit

Permalink
Merge pull request #13715 from hashicorp/dev-nsd-checks
Browse files Browse the repository at this point in the history
client: add support for checks in nomad services
  • Loading branch information
shoenig committed Jul 21, 2022
2 parents 2b9cebd + 24dcd1d commit 4508af8
Show file tree
Hide file tree
Showing 47 changed files with 3,633 additions and 331 deletions.
3 changes: 3 additions & 0 deletions .changelog/13715.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
client: Add built-in support for checks on nomad services
```
26 changes: 24 additions & 2 deletions client/alloc_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import (
"io"
"time"

metrics "github.com/armon/go-metrics"
"github.com/armon/go-metrics"
"github.com/hashicorp/go-msgpack/codec"

"github.com/hashicorp/nomad/acl"
cstructs "github.com/hashicorp/nomad/client/structs"
"github.com/hashicorp/nomad/helper"
Expand Down Expand Up @@ -137,6 +136,29 @@ func (a *Allocations) Stats(args *cstructs.AllocStatsRequest, reply *cstructs.Al
return nil
}

// Checks is used to retrieve nomad service discovery check status information.
func (a *Allocations) Checks(args *cstructs.AllocChecksRequest, reply *cstructs.AllocChecksResponse) error {
defer metrics.MeasureSince([]string{"client", "allocations", "checks"}, time.Now())

// Get the allocation
alloc, err := a.c.GetAlloc(args.AllocID)
if err != nil {
return err
}

// Check read-job permission
if aclObj, aclErr := a.c.ResolveToken(args.AuthToken); aclErr != nil {
return aclErr
} else if aclObj != nil && !aclObj.AllowNsOp(alloc.Namespace, acl.NamespaceCapabilityReadJob) {
return nstructs.ErrPermissionDenied
}

// Get the status information for the allocation
reply.Results = a.c.checkStore.List(alloc.ID)

return nil
}

// exec is used to execute command in a running task
func (a *Allocations) exec(conn io.ReadWriteCloser) {
defer metrics.MeasureSince([]string{"client", "allocations", "exec"}, time.Now())
Expand Down
87 changes: 87 additions & 0 deletions client/alloc_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
nconfig "github.com/hashicorp/nomad/nomad/structs/config"
"github.com/hashicorp/nomad/plugins/drivers"
"github.com/hashicorp/nomad/testutil"
"github.com/shoenig/test/must"
"github.com/stretchr/testify/require"
"golang.org/x/sys/unix"
)
Expand Down Expand Up @@ -529,6 +530,92 @@ func TestAllocations_Stats_ACL(t *testing.T) {
}
}

func TestAlloc_Checks(t *testing.T) {
ci.Parallel(t)

client, cleanup := TestClient(t, nil)
t.Cleanup(func() {
must.NoError(t, cleanup())
})

now := time.Date(2022, 3, 4, 5, 6, 7, 8, time.UTC).Unix()

qr1 := &nstructs.CheckQueryResult{
ID: "abc123",
Mode: "healthiness",
Status: "passing",
Output: "nomad: http ok",
Timestamp: now,
Group: "group",
Task: "task",
Service: "service",
Check: "check",
}

qr2 := &nstructs.CheckQueryResult{
ID: "def456",
Mode: "readiness",
Status: "passing",
Output: "nomad: http ok",
Timestamp: now,
Group: "group",
Service: "service2",
Check: "check",
}

t.Run("alloc does not exist", func(t *testing.T) {
request := cstructs.AllocChecksRequest{AllocID: "d3e34248-4843-be75-d4fd-4899975cfb38"}
var response cstructs.AllocChecksResponse
err := client.ClientRPC("Allocations.Checks", &request, &response)
must.EqError(t, err, `Unknown allocation "d3e34248-4843-be75-d4fd-4899975cfb38"`)
})

t.Run("no checks for alloc", func(t *testing.T) {
alloc := mock.Alloc()
must.NoError(t, client.addAlloc(alloc, ""))

request := cstructs.AllocChecksRequest{AllocID: alloc.ID}
var response cstructs.AllocChecksResponse
err := client.ClientRPC("Allocations.Checks", &request, &response)
must.NoError(t, err)
must.MapEmpty(t, response.Results)
})

t.Run("two in one alloc", func(t *testing.T) {
alloc := mock.Alloc()
must.NoError(t, client.addAlloc(alloc, ""))
must.NoError(t, client.checkStore.Set(alloc.ID, qr1))
must.NoError(t, client.checkStore.Set(alloc.ID, qr2))

request := cstructs.AllocChecksRequest{AllocID: alloc.ID}
var response cstructs.AllocChecksResponse
err := client.ClientRPC("Allocations.Checks", &request, &response)
must.NoError(t, err)
must.MapEq(t, map[nstructs.CheckID]*nstructs.CheckQueryResult{
"abc123": qr1,
"def456": qr2,
}, response.Results)
})

t.Run("ignore unrelated alloc", func(t *testing.T) {
alloc1 := mock.Alloc()
must.NoError(t, client.addAlloc(alloc1, ""))

alloc2 := mock.Alloc()
must.NoError(t, client.addAlloc(alloc2, ""))
must.NoError(t, client.checkStore.Set(alloc1.ID, qr1))
must.NoError(t, client.checkStore.Set(alloc2.ID, qr2))

request := cstructs.AllocChecksRequest{AllocID: alloc1.ID}
var response cstructs.AllocChecksResponse
err := client.ClientRPC("Allocations.Checks", &request, &response)
must.NoError(t, err)
must.MapEq(t, map[nstructs.CheckID]*nstructs.CheckQueryResult{
"abc123": qr1,
}, response.Results)
})
}

func TestAlloc_ExecStreaming(t *testing.T) {
ci.Parallel(t)
require := require.New(t)
Expand Down
Loading

0 comments on commit 4508af8

Please sign in to comment.