2023-03-28 18:39:22 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
2023-08-11 13:12:13 +00:00
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
2023-03-28 18:39:22 +00:00
|
|
|
|
2020-09-30 21:38:13 +00:00
|
|
|
package health
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
|
2023-04-14 16:24:46 +00:00
|
|
|
"github.com/hashicorp/consul/agent/rpcclient"
|
2022-10-24 18:09:53 +00:00
|
|
|
"google.golang.org/grpc/connectivity"
|
|
|
|
|
2020-09-30 21:38:13 +00:00
|
|
|
"github.com/hashicorp/consul/agent/cache"
|
|
|
|
"github.com/hashicorp/consul/agent/structs"
|
2021-02-25 21:22:30 +00:00
|
|
|
"github.com/hashicorp/consul/agent/submatview"
|
2023-02-17 21:14:46 +00:00
|
|
|
"github.com/hashicorp/consul/proto/private/pbsubscribe"
|
2020-09-30 21:38:13 +00:00
|
|
|
)
|
|
|
|
|
2021-04-27 19:39:26 +00:00
|
|
|
// Client provides access to service health data.
|
2020-09-30 21:38:13 +00:00
|
|
|
type Client struct {
|
2023-04-14 16:24:46 +00:00
|
|
|
rpcclient.Client
|
2021-02-25 21:22:30 +00:00
|
|
|
}
|
|
|
|
|
2022-10-24 18:09:53 +00:00
|
|
|
// IsReadyForStreaming will indicate if the underlying gRPC connection is ready.
|
|
|
|
func (c *Client) IsReadyForStreaming() bool {
|
|
|
|
conn := c.MaterializerDeps.Conn
|
|
|
|
if conn == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return conn.GetState() == connectivity.Ready
|
|
|
|
}
|
|
|
|
|
2020-09-30 21:38:13 +00:00
|
|
|
func (c *Client) ServiceNodes(
|
|
|
|
ctx context.Context,
|
|
|
|
req structs.ServiceSpecificRequest,
|
|
|
|
) (structs.IndexedCheckServiceNodes, cache.ResultMeta, error) {
|
2022-05-25 20:20:17 +00:00
|
|
|
// Note: if MergeCentralConfig is requested, default to using the RPC backend for now
|
|
|
|
// as the streaming backend and materializer does not have support for merging yet.
|
|
|
|
if c.useStreaming(req) && (req.QueryOptions.UseCache || req.QueryOptions.MinQueryIndex > 0) && !req.MergeCentralConfig {
|
2021-07-27 21:55:00 +00:00
|
|
|
c.QueryOptionDefaults(&req.QueryOptions)
|
|
|
|
|
2021-04-19 21:51:21 +00:00
|
|
|
result, err := c.ViewStore.Get(ctx, c.newServiceRequest(req))
|
|
|
|
if err != nil {
|
|
|
|
return structs.IndexedCheckServiceNodes{}, cache.ResultMeta{}, err
|
|
|
|
}
|
2021-06-28 21:29:23 +00:00
|
|
|
meta := cache.ResultMeta{Index: result.Index, Hit: result.Cached}
|
|
|
|
return *result.Value.(*structs.IndexedCheckServiceNodes), meta, err
|
2021-04-19 21:51:21 +00:00
|
|
|
}
|
|
|
|
|
2020-09-30 21:38:13 +00:00
|
|
|
out, md, err := c.getServiceNodes(ctx, req)
|
|
|
|
if err != nil {
|
|
|
|
return out, md, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: DNSServer emitted a metric here, do we still need it?
|
|
|
|
if req.QueryOptions.AllowStale && req.QueryOptions.MaxStaleDuration > 0 && out.QueryMeta.LastContact > req.MaxStaleDuration {
|
|
|
|
req.AllowStale = false
|
2022-12-14 15:24:22 +00:00
|
|
|
err := c.NetRPC.RPC(context.Background(), "Health.ServiceNodes", &req, &out)
|
2020-09-30 21:38:13 +00:00
|
|
|
return out, cache.ResultMeta{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return out, md, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) getServiceNodes(
|
|
|
|
ctx context.Context,
|
|
|
|
req structs.ServiceSpecificRequest,
|
|
|
|
) (structs.IndexedCheckServiceNodes, cache.ResultMeta, error) {
|
|
|
|
var out structs.IndexedCheckServiceNodes
|
|
|
|
if !req.QueryOptions.UseCache {
|
2022-12-14 15:24:22 +00:00
|
|
|
err := c.NetRPC.RPC(context.Background(), "Health.ServiceNodes", &req, &out)
|
2020-09-30 21:38:13 +00:00
|
|
|
return out, cache.ResultMeta{}, err
|
|
|
|
}
|
|
|
|
|
2021-04-19 21:51:21 +00:00
|
|
|
raw, md, err := c.Cache.Get(ctx, c.CacheName, &req)
|
2020-09-30 21:38:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return out, md, err
|
|
|
|
}
|
|
|
|
|
|
|
|
value, ok := raw.(*structs.IndexedCheckServiceNodes)
|
|
|
|
if !ok {
|
|
|
|
panic("wrong response type for cachetype.HealthServicesName")
|
|
|
|
}
|
2021-02-03 17:23:10 +00:00
|
|
|
|
2021-02-08 23:54:37 +00:00
|
|
|
return *value, md, nil
|
2021-02-08 16:53:18 +00:00
|
|
|
}
|
2021-02-23 17:52:54 +00:00
|
|
|
|
|
|
|
func (c *Client) Notify(
|
|
|
|
ctx context.Context,
|
|
|
|
req structs.ServiceSpecificRequest,
|
|
|
|
correlationID string,
|
2022-05-20 14:47:40 +00:00
|
|
|
cb cache.Callback,
|
2021-02-23 17:52:54 +00:00
|
|
|
) error {
|
2021-04-19 21:51:21 +00:00
|
|
|
if c.useStreaming(req) {
|
|
|
|
sr := c.newServiceRequest(req)
|
2022-05-20 14:47:40 +00:00
|
|
|
return c.ViewStore.NotifyCallback(ctx, sr, correlationID, cb)
|
2021-04-19 21:51:21 +00:00
|
|
|
}
|
|
|
|
|
2022-05-20 14:47:40 +00:00
|
|
|
return c.Cache.NotifyCallback(ctx, c.CacheName, &req, correlationID, cb)
|
2021-04-19 21:51:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) useStreaming(req structs.ServiceSpecificRequest) bool {
|
2024-03-15 19:44:51 +00:00
|
|
|
return c.UseStreamingBackend &&
|
|
|
|
!req.Ingress &&
|
|
|
|
// Streaming is incompatible with NearestN queries (due to lack of ordering),
|
|
|
|
// so we can only use it if the NearestN would never work (Node == "")
|
|
|
|
// or if we explicitly say to ignore the Node field for queries (agentless xDS).
|
|
|
|
(req.Source.Node == "" || req.Source.DisableNode)
|
2021-04-19 21:51:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) newServiceRequest(req structs.ServiceSpecificRequest) serviceRequest {
|
|
|
|
return serviceRequest{
|
|
|
|
ServiceSpecificRequest: req,
|
|
|
|
deps: c.MaterializerDeps,
|
2021-04-05 18:23:00 +00:00
|
|
|
}
|
2021-02-23 17:52:54 +00:00
|
|
|
}
|
2021-02-25 21:22:30 +00:00
|
|
|
|
2023-04-14 16:24:46 +00:00
|
|
|
var _ submatview.Request = (*serviceRequest)(nil)
|
2021-04-22 17:40:12 +00:00
|
|
|
|
2021-02-25 21:22:30 +00:00
|
|
|
type serviceRequest struct {
|
|
|
|
structs.ServiceSpecificRequest
|
2023-04-14 16:24:46 +00:00
|
|
|
deps rpcclient.MaterializerDeps
|
2021-02-25 21:22:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r serviceRequest) CacheInfo() cache.RequestInfo {
|
|
|
|
return r.ServiceSpecificRequest.CacheInfo()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r serviceRequest) Type() string {
|
2021-06-08 22:04:38 +00:00
|
|
|
return "agent.rpcclient.health.serviceRequest"
|
2021-02-25 21:22:30 +00:00
|
|
|
}
|
|
|
|
|
peering: initial sync (#12842)
- Add endpoints related to peering: read, list, generate token, initiate peering
- Update node/service/check table indexing to account for peers
- Foundational changes for pushing service updates to a peer
- Plumb peer name through Health.ServiceNodes path
see: ENT-1765, ENT-1280, ENT-1283, ENT-1283, ENT-1756, ENT-1739, ENT-1750, ENT-1679,
ENT-1709, ENT-1704, ENT-1690, ENT-1689, ENT-1702, ENT-1701, ENT-1683, ENT-1663,
ENT-1650, ENT-1678, ENT-1628, ENT-1658, ENT-1640, ENT-1637, ENT-1597, ENT-1634,
ENT-1613, ENT-1616, ENT-1617, ENT-1591, ENT-1588, ENT-1596, ENT-1572, ENT-1555
Co-authored-by: R.B. Boyer <rb@hashicorp.com>
Co-authored-by: freddygv <freddy@hashicorp.com>
Co-authored-by: Chris S. Kim <ckim@hashicorp.com>
Co-authored-by: Evan Culver <eculver@hashicorp.com>
Co-authored-by: Nitya Dhanushkodi <nitya@hashicorp.com>
2022-04-21 22:34:40 +00:00
|
|
|
func (r serviceRequest) NewMaterializer() (submatview.Materializer, error) {
|
2022-07-12 10:37:48 +00:00
|
|
|
view, err := NewHealthView(r.ServiceSpecificRequest)
|
2021-04-19 22:47:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
peering: initial sync (#12842)
- Add endpoints related to peering: read, list, generate token, initiate peering
- Update node/service/check table indexing to account for peers
- Foundational changes for pushing service updates to a peer
- Plumb peer name through Health.ServiceNodes path
see: ENT-1765, ENT-1280, ENT-1283, ENT-1283, ENT-1756, ENT-1739, ENT-1750, ENT-1679,
ENT-1709, ENT-1704, ENT-1690, ENT-1689, ENT-1702, ENT-1701, ENT-1683, ENT-1663,
ENT-1650, ENT-1678, ENT-1628, ENT-1658, ENT-1640, ENT-1637, ENT-1597, ENT-1634,
ENT-1613, ENT-1616, ENT-1617, ENT-1591, ENT-1588, ENT-1596, ENT-1572, ENT-1555
Co-authored-by: R.B. Boyer <rb@hashicorp.com>
Co-authored-by: freddygv <freddy@hashicorp.com>
Co-authored-by: Chris S. Kim <ckim@hashicorp.com>
Co-authored-by: Evan Culver <eculver@hashicorp.com>
Co-authored-by: Nitya Dhanushkodi <nitya@hashicorp.com>
2022-04-21 22:34:40 +00:00
|
|
|
deps := submatview.Deps{
|
2021-04-19 22:47:07 +00:00
|
|
|
View: view,
|
2021-02-25 21:22:30 +00:00
|
|
|
Logger: r.deps.Logger,
|
2022-07-12 10:37:48 +00:00
|
|
|
Request: NewMaterializerRequest(r.ServiceSpecificRequest),
|
peering: initial sync (#12842)
- Add endpoints related to peering: read, list, generate token, initiate peering
- Update node/service/check table indexing to account for peers
- Foundational changes for pushing service updates to a peer
- Plumb peer name through Health.ServiceNodes path
see: ENT-1765, ENT-1280, ENT-1283, ENT-1283, ENT-1756, ENT-1739, ENT-1750, ENT-1679,
ENT-1709, ENT-1704, ENT-1690, ENT-1689, ENT-1702, ENT-1701, ENT-1683, ENT-1663,
ENT-1650, ENT-1678, ENT-1628, ENT-1658, ENT-1640, ENT-1637, ENT-1597, ENT-1634,
ENT-1613, ENT-1616, ENT-1617, ENT-1591, ENT-1588, ENT-1596, ENT-1572, ENT-1555
Co-authored-by: R.B. Boyer <rb@hashicorp.com>
Co-authored-by: freddygv <freddy@hashicorp.com>
Co-authored-by: Chris S. Kim <ckim@hashicorp.com>
Co-authored-by: Evan Culver <eculver@hashicorp.com>
Co-authored-by: Nitya Dhanushkodi <nitya@hashicorp.com>
2022-04-21 22:34:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return submatview.NewRPCMaterializer(pbsubscribe.NewStateChangeSubscriptionClient(r.deps.Conn), deps), nil
|
2021-02-25 21:22:30 +00:00
|
|
|
}
|